2024-03-28
CQRs Architecture by Fowler

Answer to the article CQRS Is an Anti-Pattern for DDD

https://sylvainleroy.com/wp-admin/options-general.php?page=ad-inserter.php#tab-2

This is an answer to the benefits and drawbacks of CQRS under the light of the DDD practices.

The original written by Hristiyan Pehlivanov can be found there.

Despite the provoking title, the is well-balanced and you may find valuable insights there.

TL;DR : CQRS is not a silver bullet.

Contents

CQRS is not a silver bullet

Well, it’s obvious and despite what may say any Software theorist, there is no perfect design pattern, architecture model that fits for all.

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

  1. CQS: Command and Query Segregation
  2. Read and Write model
  3. Read and Write Datastores
  4. 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 storage.

The read model is optimized to returns directly the 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 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 storages.

The implementation 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.

Conclusion

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 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.

Other resources


 

 

 

Sylvain Leroy

Senior Software Quality Manager and Solution Architect in Switzerland, I have previously created my own company, Tocea, in Software Quality Assurance. Now I am offering my knowledge and services in a small IT Consulting company : Byoskill and a website www.byoskill.com Currently living in Lausanne (CH)

View all posts by Sylvain Leroy →