What is CQRS? How Important Is it in Microservices?
CQRS is a Microservice architecture pattern, it stands for Command and Query Responsibility Segregation.
The basic idea behind this pattern is to keep write operations separate from the read operations. Instead of using one datastore to perform CRUD operations, the read operation is performed on one datastore, and create/update is performed on a different datastore.
CQRS promises stability and scalability to large-scale applications along with significant performance improvement.
Bertrand Meyer designed CQRS pattern as part of developing the Eiffel programming language. Greg Young coined the term “CQRS.”
Where is CQRS Used?
CQRS is generally preferred in large-scale distributed applications that are ready-heavy. That is if the number of reads in the application far outnumbers the number of write operations.
Another suitable use case for this pattern is when the read operations are heavy and the datastore replicas could be placed near the geolocation where the application receives high traffic thus improving the performance of database read operations.
How is CQRS implemented?
By now we understand that to implement CQRS we will use two data repositories.
Usually, a relational database is preferred for write operations, where all the data constraints can be applied and a NoSQL database is used to support read operations. This means, that two different data models are used for write and read operations.
Naturally, the question one would have in mind is how is the data in these two repositories synced?
That’s where event-driven architecture comes into the picture.
Consider a scenario where a User calls a POST API, a few validations are performed and then the data along with its constraints are updated into our relational database.
Once the data is inserted into the DB a trigger is invoked that would update all the event handlers (event-driven architecture) to update the read database.
When another User calls a GET API, the application would retrieve the related data from the NoSQL database and send it back to the User.
The most popular Java-based framework to implement CQRS is Axon.
Axon uses commands and handlers to invoke and handle the corresponding events.
Find a simple implementation of the Axon framework and the complete code on GitHub. (Leave a star on the repository if you like the work 😃).
What are the advantages of using CQRS?
Major benefits of using CQRS include,
Flexible Scaling — Read and Write data stores can be scaled independently based on the demand.
Simple Queries — As the data models for both read and write are separate, the read data models can be designed in a way that avoids complex queries.
Improved Performance — By optimizing the read operations and placing the data store replicas across various geolocations, the read-heavy operations can improve performance significantly.
What are the disadvantages of using CQRS?
Complexity — Event-driven systems are complex to build and maintain. Even though there are frameworks like Axon that would take care of database writes, publishing events, and notifying various handlers, there have been cases where the read and write models go out of sync.
Consistency — CQRS and the event-driven architecture is designed to make sure that data is consistent across all the systems involved but in the case of message failures, the read database will go out of sync.
In cases where the number of reads is much greater than that of writes, CQRS is a pattern to be considered. Especially in cases where the read operations are to be highly optimized and need not perform any business logic or validation. A read operation is as simple as fetching the data, loading the DTO, and passing it to the client.
The biggest challenge in implementing such a system is to make sure that the data duplication is avoided, the read database never goes stale, and that all the entries made to the write database are eventually also updated in the read database consistently.
Even though conceptually, it sounds simple, CQRS is not so easy to implement and debug.