Introduction

The first time this term appeared in Software Engineering lexicons was all the way back in 1997. It appeared in the book “Object-Oriented Software Construction” by Bertrand Meyer. This software architecture is designed to mitigate known caveats of Object-Oriented architecture. Specifically in this case these caveats are:

Writes and reads generate, almost all the times, a very different load to the system.

Writing operations are often much more complicated than reads. Furthermore, they affect different scopes of the application . Write operations needs to guarantee that correct, validated and consistent data reaches the store correctly.

. Write operations needs to guarantee that correct, validated and consistent data reaches the store correctly. Security concerns are also different between write and read operations.

are also different between write and read operations. The way data is normally stored in the database is in a 3NF or something optimized and close to it. When we read data, we are usually building views to provide the user with readable data. Therefore the data presented to the user is hardly ever normalized. This is also referred to as denormalized data.

Through this very simple list, you can see that reading and writes operate in different ways, they have different concerns, they generate different performance concerns, and they generate different loads which can put a lot of strain on the system in very different ways.

CQRS is also a form of DDD(Domain Driven Design). It wasn’t initially thought out to be an actual DDD. However, through development it always forces architects to think about design first. Every application has at least one bounded context. Bounded contexts are difficult to define but essentially, they isolate a responsibility of the application like for example, handing of debit cards, library book archiver, patient data. The latter for example can be divided into multiple subdomains. There could be a separate domain to keep track of chronic illnesses like HIV and another different one to keep track of the common flu. Both of them have different data concerns. Being a chronic disease, HIV patients will need to keep track of a lot more data like T-Cell count, virus load, and other blood data for a lifetime. Patients with the flu don’t need so much monitoring. There are a lot more privacy concerns related to the first domain than the later. Assessing domains is a bit of an art and it requires the analytical skills of the engineer to determine them.

Once you have defined your domain, it’s time to start the design of CQRS for it. As you may have already seen, this design has as its main concern, the separation of read operations and write operations. Write operations cannot be read operations. In the same way, “read” operations also cannot be “write” operations.

Commands

Commands are defined as any operation that can mutate the data without returning a value. In essence these are all write operations. In CRUD terms, these are the Creation, Update and Delete operations. You may also refer to them as CUD.

Queries

Queries are defined as any operation that will never mutate data and will always return. In the end these are basically all read operations. Query operations are only read operations. They are only the R in CRUD terms.

Models

There are many ways to implement CQRS. The point is always to keep read operations apart from write operations as much as possible. In our implementation we are also going to separate operations and use Event Sourcing. This will allow us to further separate the medium where we are going to keep our data. We will use two different databases. One database will be a part of the command flows and the other database will be part of the read flows.

Implementation

Let’s first have a look at how all the moving parts will work:

In this example, I’m going to try to make this as simple as possible. There are much more elaborated options out there. There are more complicated, dynamic and scalable options out there. One of these options would be to use RabbitMQ or any other kind of message queueing system to further decouple all components. However, that leads the attention off the scope of this tutorial. The point here is to present a solution with all the fundamental points of CQRS at its core.

Here are all the dependencies we are going to need:

Notice that I am using the axon framework. This is on the most popular frameworks to implement a few things that match really well the CQRS design. Namely, we are going to see how EventHandlers and Aggregators work, how the EventBus works and how the CommandBus works. We are also going to see how this works with MongoDB in order to, in the end, get our database updated with new data.

Core

In this module I’m going to consider everything that would be common to the application. I could also have named this module as common. For our application to run, we are going to need to consider a few important things. Given its complexity, I am only going to implement a read all operation and a save operation. These will be my query and my command respectively. We’ll need a DTO to get our data into our system:

Sending data via a writer is an operation that needs to be understood by the reader and also by the writer. Our only common command should be located here:

Finally, we know from the schema we saw, that both the “write service” and the “read service” will need to have access to the EventStore. This essentially is, at the end of the tail, our MongoDB database. Axon has very nice out-of-the-box libraries that allow us to easily implement this Event Sourcing mechanism. this is part of the reason why I chose this. It makes for a very simple form of implementation:

Command Service

First, we need to implement a representation of our command. In the case of our command service we only have a command to add further video series. Therefore, our command has the same properties as the actual series. Note the id field:

The id field is indeed a String. This is essentially our operation ID. It can be implemented in several ways. We just need to make sure that it is always a unique string, number or whatever we choose.

Now it’s time to implement the aggregator which will send our command through the command bus and make it reach our command handler:

Notice the EventSourcingHandler. It doesn’t seem to be doing much, but remember that in this code section you are looking at the contents of the Aggregate element. If you look at the mongo database you will find something like this:

Notice the aggregateIndentifier. That is your id. You need the EventSourcingHandler in order to complete the request and have your Event sourced to the EventStore.

Now we only need to complete our application by implementing a Controller:

Notice that we are injecting a CommandGateway. This is precisely the gateway that allows us to send commands into our system.

Finally, the Spring Boot Launcher:

To complete our application we still need to configure our Spring Boot Launcher:

Query Service

The query service is essentially a reader of the EventStore and will act upon it without the user intervention. The query service needs to perform queries. In this way, I implemented a command to do that just that:

Notice that this command ended up being just an empty class. That is done on purpose. We do not need parameters to pass through a read all operation, but we do need its representation.

Because we are accessing a database and storing records, we now need to implement the Entity responsible for this data:

As you may already have guessed, in this implementation we are going to use JPA repositories:

In the query side, we have EventHandlers which are very similar in shape with the Aggregate. The difference, of course, is that they process immediately once they get an event or a command:

Notice that instead of CommandHandler, we now have QueryHandler. Instead of EventSourcingHandler we now have EventHandler. There are annotations used to distinguish what happens in the command service and in the query service respectively. Also, the id isn’t there. The id isn’t important because no data will be going to the event store. All the data is being handled directly with the JPA repositories.

We can now focus our attention on the Controller for the query service controller:

And finally our Query Launcher:

To complete our application we need to configure it:

Give it some structure:

And finally some data:

We are finally ready to make some tests. What I did for testing is very simple. First I performed a request to see all my current data:

$ curl localhost:8090/video-series [{"name":"Modern Family","volumes":12,"cashValue":12.3,"genre":"SITCOM"},{"name":"Six Feet Under","volumes":10,"cashValue":34.3,"genre":"DRAMA"},{"name":"Queer as Folk","volumes":24,"cashValue":55.3,"genre":"DRAMA"}]

As you can see, we get three series. Let’s add a new one:

$ curl -d '{ "name":"True Blood", "volumes":30, "cashValue": 1323.2, "genre": "Bloody"}' -H "Content-Type: application/json" -X POST http://localhost:8080/video-series

You should now see:

$ curl localhost:8090/video-series [{"name":"Modern Family","volumes":12,"cashValue":12.3,"genre":"SITCOM"},{"name":"Six Feet Under","volumes":10,"cashValue":34.3,"genre":"DRAMA"},{"name":"Queer as Folk","volumes":24,"cashValue":55.3,"genre":"DRAMA"},{"name":"True Blood","volumes":30,"cashValue":1323.2,"genre":"Bloody"}

Conclusion

Note that although we can see that this works, it’s very important that you understand what happened behind the curtain for this application. The separation between the “write” and “read” operations and the fact that they are named command and query operations respectively is what makes the foundations of this architecture. The more decoupled the architecture is designed, the better it is. There are thousands of corner cases and special situations in the landscape of DDD and CQRS. Event sourcing is just one of the ways to get this implemented. In our example we used Spring, SpringBoot and Axon to get our commands and events across our network. We didn’t use any messaging queuing system. I do intend to write another article on that, but that will be for later. For the time being I hope you have enjoyed this tutorial about this very simple example.

I have placed all the source code of this application in GitLab

I hope that you have enjoyed this article as much as I enjoyed writing it.

I’d love to hear your thoughts on it, so please leave your comments below.

Thanks in advance for your help and thank you for reading!

References

[1]

[2]

[3]

[4]

[5]

[6]