Command Query Responsibility Segregation (CQRS) is an architectural design pattern whose origins are attributed to Greg Young. It is based on Bertrand Meyer’s Command and Query Separation Principle (CQS), according to which a method should either be a command that performs an action, or a query that returns data, but never both at the same time.
CQRS – what it is
Following this train of thought, CQRS’s key principle is to strictly separate querying from executing commands. Thus, what has been a single service in classical architectural models now is split into two separate services: a command service accommodating the command methods (methods that change the state of the system) and a query service accommodating the query methods (methods providing a view of the system’s state without changing the state itself).
The result are two services which are completely independent from each other and can be designed and scaled according to their different needs.
Being able to scale each side independently is important because the number of queries is typically much higher than the number of commands. Also, when it comes to data storage, the two sides have completely different requirements. Whereas commands need to store their data in normalized tables, the data storage on the query side should be optimized to enable fast reads. Since normalized data requires complex, time-consuming queries, CQRS usually uses separate, de-normalized data tables for the query side, thus minimizing the number of joins needed for a query to get the requested information.
Of course, if we decide to work with different data storages for querying and commanding, we also need to provide a mechanism that ensures consistency across all tables.
Example: Booking system
Let me illustrate the CQRS pattern by the example of a simple booking system that allows the user to make room reservations, to get an overview about which rooms are still vacant and which not, and to view a list of all the bookings he has made.
In the system, our basic queries are getAllBookings and getBookingsByUserId. Each query is executed by its own query object and returns the information requested by the client as its search result. A query can also carry search criteria and paging options.
On the command side, we have the commands CeateBookingCommand, EditBookingCommand and DeleteBookingCommand, which are issued by the user through UI elements. Each command is modelled in a separate class and contains the minimal amount of information that is necessary for executing it (for booking a room, we would need the date, number of people, name, contact details, etc, depending on the business logic).
For each of these commands, the command model provides a separate command handler whose responsibility it is to validate the command and to make changes in the persistent data storage. If, for example, the user issues a CreateBookingCommand, the command handler assigned to deal with this kind of command will first validate the command (e.g. check if all required attributes such as date and user name are provided, check if the email address is in a correct format, etc.) and check if the requested room is still available. If everything is ok, it will then save the new booking into the database. Even though commands do not return data by definition, they may issue status and/or error messages in order to let the user know if the request was processed successfully.
With the distinction between command and query services and the separation of querying and commanding concerns, we are able to create a solution that is optimized for either sides. The advantages of such a system can be summed up as follows:
- Scalability: scale command and query sides independently from each other
- Flexibility: CQRS allows the use of different representations of objects for commands and querying and is therefore more flexible than CRUD, which requires using the same classes for queries and commands
- Better performance: separation allows optimizing each operation (e.g. quicker querying through de-normalized data storage)
- Testability: separation of concerns leads to better testability
- Maintainability: independent elements are easier to adapt to changing business requirements and thus easier to maintain
- Collaborative systems: CQRS is particularly useful in collaborative systems in which multiple users operate on the shared data simultaneously. If any conflicts arise, it might not always be desirable to apply the rule ‘last one wins’. CQRS allows defining more complex rules that capture the business logic.
Summary: when to use and when not to use
Even though the list of advantages above looks intriguing, not every project is suited for applying CQRS. CQRS should primarily be used in highly collaborative and complex systems in which the underlying business rules change frequently. Simple, static or non-collaborative systems, however, usually do not benefit from using the CQRS pattern.
The CQRS pattern has been very popular and widely used since it has been introduced, but that does not mean that it should be used everywhere. To put it in Udi Dahan’s words, “Most people using CQRS […] shouldn’t have done so”. So, before employing CQRS in any project of your own, you should carefully reflect what will be gained from using it.