Past few articles I have been discussing about Microservices. When we try to search around Microservices, CQRS is a jargon used in majority of contents available on internet. In this article, we will try to understand basics about this pattern.
CQRS – Problem Statement and Context !
Traditional architectures often use a single model for both read and write operations. It is simpler to implement as well as simpler to understand. Some applications are small to medium sized and they have almost same proportions of read and write calls. However, some applications may have huge read calls and very few write calls. Some examples may be – a website which shows live score – millions of people are looking at the page which reads the data, but there may be only few stations from which the data is being updated.
For such applications, where the read operations and write operations are not really in equal amounts,
- They may want to scale the application in such a way that they want to scale read and write operations independently of one another.
- Also, they may want to further improve the performance of read calls by de-normalizing some data or creating views.
For achieving this, we can use CQRS pattern. The full from is Command-Query-Segregation pattern. As the name suggests, the command (write part) and query (read part) are separate processes – thus they can be scaled independently. Also, it would lead to better maintainability as the code is segregated.
Basic Terminology
Let’s try to understand basic terminology associated with CQRS pattern.
Commands – the write side
Generally the “write side” is a separate API process which is responsible for creating / modifying data in database. Generally the “write side” consists of two types of components:
- Commands – which are models and hold the data to be created / updated / modified in the database.
- Command handlers – which are responsible for performing the actions intended by commands.
Commands are named according to the task which needs to be performed. For example, for adding items to shopping cart, there may be command with name AddToShoppingCartCommand and it may have a handler AddToShoppingCartCommandHandler.
Query – the read side
Generally, the “read side” is also a separate API process which is responsible for reading the data. The data may be read either from the same set of database and tables which were used by the commands or there may be a separate de-normalized database, which is optimized for efficient querying. If queries are being read from separate database or tables which are optimized for read operations, then there may be some kind of synchronization logic, a background component responsible for updating data from “write side” database to “read side” database.
The read side as well has two important types of components
- Queries – These are models which hold the parameters which should be
- Models – These are to hold the data returned by query endpoint. This is what the API returns.
- Query Handlers – These are classes which are responsible for handling the query requests and return the appropriate response.
Event Sourcing – Optional Enhancement
This is an optional pattern which can be combined with CQRS. Here the “write side” does not actually writes data to the database, but it just creates sequence of events as they were happening in the application. For example, traditionally, when a user is created , a row is saved in database. When that same user is updated, the same row is updated in the database. But in case of event sourcing, two separate events are logged.
So instead storing the latest state, stores a sequence of events as they happened in the application. This improves auditability of the system. Rolling back to certain point in time may seem easier. But important point to note is – this will lead to eventual consistency. This means read side may have stale data because construction of optimized data for read operations from these events would take some time.
Event sourcing we can discuss in separate article.
When to Use CQRS ?
✅ Ideal for:
- Complex business logic requiring scalability.
- High-performance applications with heavy read and write loads.
- Systems that require strong auditability.
❌ Avoid for:
- Simple CRUD applications without complex business rules.
- Systems with minimal scalability concerns.
Caution and Considerations
Until now, we have seen all positives about this pattern. Let’s discuss what inconveniences it may bring along with the advantages:
- Increased complexity – This pattern may increase complexity of implementation. How much ? Well it depends on how you have structured entities, database and business logic. It depends on level of interdependencies between the domains.
- Messaging and Synchronization Challenges – If read and write databases are separated, then a background process may be needed to synchronize the data. That synchronization process may be implemented using either Observer pattern (subscribed to a message queue or service bus and then responding to events). This increases complexity as this is additional functionality in addition to core business features. Also, additional challenges about failed messages and their handling, or debugging may become tricky.
- Eventual Consistency – This is not a problem if read and write processes are using the same database. But if they are using separate databases, then there will be synchronization logic, which would be either triggered periodically or may be message (pub-sub) based. So, it means, the two databases may be out of sync for some minimal time – depending on network latencies or message processing times. This means that although data was written successfully, read API may still be showing old data. So eventual consistency is another challenge that may not be desirable for certain businesses.
Conclusion
CQRS is a powerful pattern for improving scalability, maintainability, and performance in modern applications. When combined with event sourcing, it unlocks even more potential for handling complex business scenarios. Key is to understand the challenges it may bring when adopted. This will help to keep realistic expectations after adopting this change.
🚀 Have you used CQRS in your projects? Share your experiences in the comments!
