DDD Part VI

M Yauri M Attamimi
8 min readApr 22, 2021

ES and CQRS Implementation

Hopefully it’s not that late now to write the last part of my DDD blog series. For those of You whom somehow got into this part without any chance to have a look into the other parts, You can check them out by starting from here.

ES (Event-Sourcing) and CQRS (Command-Query-Responsibility-Segregation) are not the new kids on the block. They have been there for some couple years. As for me, the first time I heard about these stuff was go back in the year of 2012. Even during that time I also knew that both (particularly ES) had been there for … not sure when exactly. Since then, I’ve been getting a chance to playing with these two “guys” several times, and lucky enough to failed several times as well :-). I always believe that from failures, we can learn so many good things about why they failed, which eventually can bring us to the next level of better understanding. That’s why as a good engineer and architect, we used to do ‘trial and error’ anyway. Even some of the great leaders from a well-known Companies won’t argue me on this matter :-) (See it here).

Back to our main topic, I won’t talk too much the nitty-gritty details about what are ES and CQRS ?, how are they working together to achieve their goals ?, and what are the benefits and drawbacks of using both ? particularly in our micro-services architecture. Nowadays you can find a lot of information about those two from the internet. However, I’d like to share that both actually came from different perspective and coined by different guy. I figured out that ES itself was brought from the concept of a “Journal Entry” in the “Accounting” world. So essentially, You can use CQRS without ES at all (even in monolith application), and similarly you can use optionally choose to implement ES without CQRS at all. However, there’s a saying that “You have to implement CQRS when you’re doing ES to make it easier”. This saying most likely came from someone that already tried to implement ES without CQRS, so they know exactly how difficult it is to use ES with “No CQRS”.

I had a chance to use CQRS several times in my non-ES architecture, yet I straightly use ES and CQRS together for my ES implementation due to that wise saying since I don’t want to reinvent the wheel.

With that being said, I will be using Java for our implementation since there’s one cool framework named Axon which already handled all of the technical complexities for us, so we can keep focus with the business functionalities to reduce our time-to-market. I’m planning to write another article later to discuss on how we do implement this ES-CQRS without any framework at all (i.e. build our own framework). But let’s use Axon for now.

As we can see from its’ website, Axon mainly comes with 2 different flavors: the Axon Framework, and the Axon Server which is used as it’s main event-store and message bus (command-bus and event-bus).

If we are searching for the tutorial on how to use this framework, you can find a lot of articles and even YouTube videos. So I think, it won’t be interesting for me to write another article for it. As a contrary, what I’m going to write here was based on my RnD in using this framework without Axon Server at all (we’ll be using PostgresDB as both our command-bus and event-store, and RabbitMQ as our event-bus). In other words, we won’t go with the Axon standard implementation. I was experiencing quite tremendous time to go with this implementation since all of the tips written on the official Axon website were not working as I expected, at least at the time when I’m writing this.

Enough the chit-chat, let’s discuss now on how we’re going to implement this.

Prerequisites

  1. Java 11 (as the project that I’m actively working at the moment uses this version).
  2. SpringBoot Initializr (https://start.spring.io).
  3. I’m using this freakingly awesome IDE, yet any editor should be fine.
  4. Docker. This one is optional, but I strongly recommend You to use it, especially if you want your machine to be not disrupted by any software installation. The most notably benefit by using it is we can easily switch between different version of software as we need due to its’ isolated environment.

Our Project Design

For simplicity in showing the concept, we’re going to build a simple user-management-service with two basic features as depicted below:

User Management Service

And based on the Onion Architecture that I had described on the Part 3, here’s what it looks like for our code scaffolding (structure components):

Our Code Structure Components

As depicted in the diagram, there’s no Axon server at all, and as replacement we’ll be using these following:

  1. PostgresDB as our User Event Store, and implicitly as our Command-Bus to dispatch all commands to their respective command handler components (aggregates). This will be representing our writer database (the command side).
  2. RabbitMQ as our Event Broker, hence all generated events will be routed to its listeners through this particular component. (We can use Kafka as well for sure, I will cover it later in the next chapter).
  3. PostgresDB as our User Query Store to store all projections as needed from the business (functional) point-of-view. This will be representing our read database (the query side).

Create Our Project

Common way to start creating our project is by generating the project through Spring Initializr. For some Java IDE such as IntelliJ IDEA Ultimate, we can create it from there as well. I won’t go details on this since its very straightforward. What I’m going to describe here is that we’re going to have 1 parent pom project with 3 submodules inside respectively for:

  1. User Core (usercore)
    This will be our core domain as represented on the above diagram. As you probably noticed, we have all of our events defined here. The main reason for it because events are something related and specific to the business domain, hence from the onion architecture perspective we have to cover it in the most inner side. And the reason for me to wrap this as an independent submodule is because we can deliver this as a Jar library to be shared later to any systems/parties that have an interest to integrate with our business domain.
  2. User Command Service (usercmdsvc)
    No need for more explanation, as You might probably guess.. this is going to be our Command (writer) service, that is letter C from the CQRS perspective.
  3. User Query Service (userquerysvc)
    Our Query (reader) service, that is letter Q from the CQRS perspective.

Creating our User Core Module

We only need to have lombok as our dependency for this particular module since this module will merely contains some POJO classes for our Domain Event and Entity, and we want to avoid any boilerplate codes such as getters and setters to produce much cleaner code.

Here’s what our pom.xml will be looked like after we create our first module for User Core:

Creating our User Command Service

Similarly, you can fire up another Spring Initializer and create our second module for Command service. And here’s our pom.xml for this particular command service module:

Here are some notably dependencies along with their explanation:

  1. Spring Web
    artifact: org.springframework.boot.spring-boot-starter-web
    We’re going to expose our API as REST APIs. This is where this particular dependency play its’ role.
  2. Spring Data JPA
    artifact:
    org.springframework.boot.spring-boot-starter-data-jpa
    We’re going to use SQL database as our main event-store, that’s the reason we added this dependency for all JPA operation.
  3. PostgreSQL Driver
    artifact: org.postgresql.postgresql
    SpringBoot brings the concept of “convention over configuration”, therefore once it detects we have JPA library as one of our dependency, it strongly needs more specific information about what kind of database we’re going to use. Hence we must add this postgresql library (depends on our database) as our database driver, or Spring will be complaining later if we don’t specify any database driver within our pom.
  4. Axon Spring Boot Starter
    artifact: org.axonframework.axon-spring-boot-starter
    Our main library for Axon framework. However, we need to exclude axon server connector as specified in the exclude tags, because we’re not going to use Axon Server.
  5. Spring Cloud Stream Binder Rabbit
    artifact: org.springframework.cloud.spring-cloud-stream-binder-rabbit
    We need this library since we use RabbitMQ as our event-broker. Spring Cloud Stream is very handy since it is agnostic, so in essence we can use it to connect with any messaging middleware.
    In our case, we use Rabbit as our binder, so Spring will provide Rabbits’ specific implementation for us.
    In the future, if we need to change our messaging middleware to something else like Kafka, we merely need to replace our Rabbit binder with Kafka binder, and the rest of our code will almost still the same.
  6. User Core
    This is our first module which we created earlier. We need to include it in our command service since we’re going to use some the domain events and entities from this particular core module.
  7. Lombok
    To reduce any boilerplate codes.

Creating our User Query Service

Here’s our pom.xml for User Query Service:

As we can see, we’ve also excluded the Axon server connector from this user query service pom.xml. Another thing which make it different from the user command service pom is that instead of using Spring Cloud Stream, we deliberately use AXON AMQP extension to connect our event handler (listener) to our RabbitMQ. Nonetheless, you can also use Spring Cloud Stream as well. It all depends on You, which one is more convenience for You.

Creating our Parent Project

  1. Create a new standard Maven project and give it a name as user-management-service (you can skip the archetype selection). You can either use your favorite IDE for this, or simply by executing this following command from your terminal:
    mvn archetype:generate -DgroupId=com.power \
    -DartifactId=user-management-service \
    -Dversion=1.0.0 -DinteractiveMode=false
  2. Once the project is created, you can delete the src folder since it’s not needed.
  3. Move the 3 modules you created earlier (usercore, usercmdsvc, userquerysvc) into this user-management-service folder.
  4. Update your user-management-service pom.xml to Introduce those 3 modules we created earlier:

At this point, your project will be looked more or less like this one:

Our Project Structure

OK, that’s all for the setup. I won’t be going further with the explanation since it’s quite explanatory from the code that you can grab from my GitHub here. Please follow my GitHub account so you’ll get notified for any future update in the code, and feel free to drop me a message in case you have some concerns or constructive feedbacks.

--

--