Answer to the Article ''CQRS Is an Anti-Pattern for DDD''
This article is an answer to the benefits and drawbacks of CQRS under the light of the DDD practices.
Join the DZone community and get the full member experience.
Join For Free
This article is an answer to the benefits and drawbacks of CQRS under the light of the DDD practices.
The original article written by Hristiyan Pehlivanov can be found here.
Despite the provoking title, the article is well-balanced and you may find valuable insights there.
TL;DR: CQRS is a not a silver bullet.
CQRS Is Not a Silver Bullet
Well, it's obvious, and despite what some software programming theorists may say, there is no perfect design pattern or architecture model that is a fit for all.
DDD, Onion Architecture, Clean architecture: these concepts rely on some good sense and one or two successful implementations in a given context.
CQRS has the same issue. Plus, there are some misconceptions about it that confuse practitioners.
CQRS Maturity Level
As I understand CQRS and practice it, I usually distinguish four levels of maturity
- CQS: Command and Query Segregation
- Read and Write model
- Read and Write Datastores
- Event sourcing
Level 1: CQS: Command and Query Segregation
This part is easy to understand. In your code, you should distinguish read and write operations and avoid mixed ones as much as possible. Your write operations should follow the Command design pattern.
I also recommend implementing a Command Bus interface that will work for the next maturity levels.
In Java, you may add the following tricks:
- Read operations are read-only transactions
- If you aren't following DDD, read operations are performed directly in your controller. You don't create services. You convert directly your queries into a JSON view.
- Write operations may use aggregates like offered by an ORM
Level 2: Read and Write Model
The second level of maturity requires to have two different models (and possibly repositories): the read model and the write model.
The architecture keeps a single data storage.
The read model is optimized to returns directly the data required by the API. For example, you are using Spring JDBC or MyBatis for the read model and Hibernate for the write model.

The write model is created by following the actions required by the business. Each command is a business use-case or operation.
The BDD or TDD approach will help you to build the necessary commands.
The execution of the commands may trigger events. The event bus and the event loop are stored into the memory of the server. Nothing complicated there.
Level 3: Read and Write Datastores
The third level of maturity takes the principles implemented in the previous stages and add another constraint: we have two separate data storages.
The implementation complexity increases and there is a major concept to be implemented:
- model synchronization: synchronously or asynchronously, the events produced by the commands must trigger a refresh of the read database
Moreover, your event system has to be externalized for example as a message bus (Kafka, Rabbitmq). It will help you to safely keep your messages being processed.
Level 4: Optional Event-Sourcing
Well, it is not a maturity level but another practice that fits with CQRS. Your commands are using the writing model to modify the write storage. Instead, your commands produce application events. These events stored into a log and can be used to replay the history of a database and eventually restore it.
More details can be found here.
The usage of Event Sourcing is totally optional to CQRS.
Conclusion
In my opinion, the benefits of CQRS are:
- Testability: Command are objects and not parameters. This way the command handlers are easy to test and to mock. The object validation is also possible on the commands using Hibernate validation.
- Functionality/Maintainability: a CQS architecture is easy to read since everything is business-oriented. No mega service or Godclass DDD repositories.
- Productivity: CQS offers a big benefit that we remove almost all DTO/Value object code and respect DRY principle. No need to have two abstractions layers between your DAO, your value object, and the JSON view you are returning. You should use a DAL optimized for read access and JSON conversion
- Fun: Almost everything has to be built from scratch. Ok it exists some (paid) frameworks but I wouldn't use them.
The drawbacks of CQRS are:
- More code: yes instead of a basic function you have to write a command object, a command handler ( your tests), validation etc. But the code is easy to read and self-explanatory
- Asynchronism issues: since your write data storage and read data storage are separated, the data synchronization may be delayed and causing UI issues for your users.
- More layers: command, command bus, event, event handlers are new concepts. Building a service or a DDD repository looks easier ( at the beginning)
- Technology abstraction: DDD modeling and CQRS are not quite compatible, more thought has to be provided on it and a compromise to be found.
Published at DZone with permission of Sylvain Leroy. See the original article here.
Opinions expressed by DZone contributors are their own.
Trending
-
Never Use Credentials in a CI/CD Pipeline Again
-
Scaling Site Reliability Engineering (SRE) Teams the Right Way
-
Design Patterns for Microservices: Ambassador, Anti-Corruption Layer, and Backends for Frontends
-
What Is mTLS? How To Implement It With Istio
Comments