Comand-Query Responsibility Segregation (CQRS)

Posted on May 17, 2023

In DDD you will clearly come over the term “Command and Query Responsibility Segregation” or also: CQRS.

This article describes this pattern and shows how it works together with Event Sourcing.

The problem

In an usual application you use the same data model for read and write operations in/from the database.

This works good in simple applications, but can be messy in more complex applications. So data transfer objects (DTO) may be introduced to return a model that fits into the query.

graph TB; View Controller Service Model db[(Storage)] View --> Controller Controller --> Service Service --> Model Controller --> |DTO| View Model -->|write| db db -->|read| Model

Unfortunately you need to map the DTOs to the read/write model. In Java you can use tools like MapStruct to solve this problem.

This is still not the best way: It can be observed that usually the read and write operations have different requirements in regards of resources and shapes of the models.

Take as an example the end of day calculation of stocks: On the write side you’ll have peaks in the write operations as really many datasets are coming in. Maybe you will also do some complex business logic before writing into the database. On the read side on the other hand you’ll just read the value - no complex operations are needed as the value is already calculated. (However, in a typical application the read side usually has a much higher load.)

The solution

CQRS solves this problem with the following pattern: The read and write sides are separated into different models and maybe also completely isolated from each other. Commands are used to write the data. Queries are used to read the data.

This is shown in the following picture: On the write side the clients access the write models with commands and aggregates. On the read side are the queries and projections, which show the domain model in different shapes.

Diagram of CQRS with commands on the write side and queries on the read side

An important thing is to behave commands really as commands and not in a data-centric way. So instead of saying set value x to y you say calculate value y.

It’s also possible to split the write and read sides of the storage and synchronize them by an own mechanism. Although the full isolation is not required, the separation of the data storage gives more flexibility: You can use different databases for the read/write models and different schemas.

Diagram of CQRS with separate, synchronized write and read storage

This makes CQRS complex: The data needs to be kept in sync. An event-driven architecture can help with that, but this leads to the typical consistency challenges that event driven architectures bring with them. However you need to implement a form of a projector that synchronizes the data.

Keeping data in sync

Keeping data in sync when the storages are different is the biggest challenge in the CQRS pattern.

There are different ways to achieve this. In CQRS this mechanism is called projection.

A rather simple way is a static projection where the projector maps the write model to the read model. This can get pretty complex.

When the write side uses the Event Sourcing pattern, it’s possible to to make use of the domain events whenever an event happens. This will tell the read side about the changes to update the data storage.

It’s also possible to implement the Saga Pattern to synchronize the storages.

CQRS and ORM

Unfortunately ORM cannot generate CQRS code. But it is possible to use ORM tools like Hibernate to build the CQRS models on top of the generated code.

CQRS and Event Sourcing

Event Sourcing (ES) and CQRS are both used in DDD. You may ask then: How do Event Sourcing and CQRS fit together? And do you need both together?

The second question can be answered with no. It can be helpful to have ES and CQRS, because ES allows you to throw Domain Events inside the aggregates. This could be used to notify changes for the read model. But you don’t need necessarily to do this as this can make the application way too complicated.

Diagram of CQRS combined with event sourcing using domain events

When to use CQRS

As CQRS scales really well in huge applications and complex domains, you can use it when you have a complex model.

Furthermore, when you have different loads on the read and write side and different models should be used for the operations, you can consider to introduce CQRS.

When you’re working in a team where one is focusing on the write side and the other one on the read side, the CQRS pattern is really great!

But these are more like general statements. You need to have a look at your special use case to check if it really fits to your problem.

Basically you should not consider it when your domain model is simple and you are developing a simple CRUD application.

Benefits and trade-offs

To make a good decision whether this patterns is a good solution for your problem, you need to know the benefits and trade-offs of it. Let’s discuss them:

Benefits

Some of the benefits are:

  • You have different models for the read and write side of your application. This allows you more flexibility and a better maintenance.

  • The two sides can scale independent from each other. While a read side may not need so much resources as the read side, you can choose different algorithms and data sources for it! So optimizing your application to your needs is easier.

  • With the separation you also have the separation of concerns.

  • You can create views on the database. This leads to simpler queries.

Trade-offs

The trade-offs are:

  • This pattern leads to code duplication as the two sides are implemented separately from each other.

  • It may be only well-suited for complex domain models. So before considering to choose this as an option you need to answer the question how complex the domain model will get.

  • You need to handle eventual consistency of your models and synchronize the read and write sides of your model. Messaging and events bring their own complexity you need to attack.

Conclusion

This article showed the concepts of the CQRS pattern and it’s benefits and trade-offs.

By separating the read and write side of you application you have a better control of the special needs in this both sides.

But you need to keep in mind that you’ll get more work in the implementation. This work will be rewarded with a better maintainability and scalability when you have a complex domain model.

You can add Event Sourcing and make use of the domain events to implement the write side of the application.

Further reading

Baeldung - CQRS and Event Sourcing in Java

Martin Fowler - CQRS

To keep the data in sync you may use the Saga Pattern.

CQRS.nu - Tutorial for applying CQRS