This article is an answer to the benefits and drawbacks of CQRS under the light of the DDD practices.
The original article written bythere.can be found
Despite the provoking title, the article is well-balanced and you may find valuable insights there.
TL;DR : CQRS is not a silver bullet.
CQRS is not a silver bullet
DDD, Onion Architecture, Clean architecture, both of 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 the 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 as much as possible mixed ones.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 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 in 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 in a log and can be used to replay the history of a database and eventually restore it.
More details can be found there.
The usage of Event Sourcing is totally optional to CQRS.
In my opinion, the benefits of CQRS are :
- Testability: Commands 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 codes and respect the 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 reading 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.
- CQRS by Azure
- CQRS and Event sourcing
- CQRS by Martin Fowler
- CQRS, l’architecture aux deux visages (partie 1)
- When to use (and not to use) CQRS
- Pourquoi avoir choisi d’utiliser l’architecture CQRS ?
Donate to the blog