An introduction to command query responsibility segregation
Optimize and scale your data models in your complex applications
Command query responsibility segregation (CQRS) is an application architecture pattern. This pattern is often used in event driven applications and is frequently associated with event sourcing. It consists of separating the logic that handles commands from the logic that handles queries.
CQRS architecture pattern
Let’s start by defining what commands and queries are. In its simplest form, a command is an operation that changes the state of the application. And, a query is an operation that reads the state of the application.
In an application, data is represented using models. Traditionally for a defined domain, a single model is used, and it handles all operations (create, read, update, and delete, or CRUD). However, as requirements evolve, the model can become complex and unmanageable.
By splitting the commands and queries, the CQRS architecture pattern allows developers to use different models to read and write data.
For example, if we have a service handling orders:
* `createOrder()` * `updateOrder()` * `cancelOrder()` * `getAllOrders()` * `getOrdersForCustomer()`
If you apply the CQRS pattern to it, the services become
* `createOrder()` * `updateOrder()` * `cancelOrder()`
* `getAllOrder()` * `getOrdersForCustomer()`
In essence, the CQRS pattern is simply separating the command and query sides. In practice, the degree of separation can vary from having 2 models within an application to having 2 applications.
The idea of separating commands and queries also comes from the fact that it’s relatively common that both are used by different actors or at different scale. For example, in the
OrderService described above, customers would primarily use the commands, while it’s the company offering the service that would use the queries.
Architecturally, a CQRS application might look like this:
In this case, the commands write updates to Apache Kafka in the corresponding topics. On the query side, Kafka Streams can be used to create projections (via aggregations or joins) of the data in the topics. You can explore a basic or extended event-driven architecture in the IBM Cloud Architecture Center.
We’ll see in the next section the benefits this can have on our application architecture.
Why use CQRS?
CQRS provides separation of concern and allows us to have simpler models focussed on different use cases. In a complex application, this can avoid having a single model that becomes unmanageable due to complicated validation logic for writes and many data transfer objects (DTO) for reads.
These models can be optimized for reads or writes and map closely to DTO. Each model can also be backed by a different persistence layer, again optimized for its role. For example, the query database can have materialized views matching DTOs and not worry about write performance.
The separation of concerns also ensures that another service that interacts with the query side cannot update data and only has read access.
The other reason CQRS is often used is scalability. When we have 2 services, they can be scaled independently to provide high performance. This is especially relevant when there is a large disparity between reads and writes.
For example, consider a container shipping company might receive thousands of orders from its customers throughout the day. However, orders will only be retrieved periodically each day.
Other considerations when using CQRS
Like any other application pattern, some care must be taken when implementing a service using CQRS.
First, applying CQRS to a service adds complexity. We end up with more models to manage, and if a microservice is split into 2 parts, 2 microservices have to be run and managed.
Also because of the very specific nature of its benefits, CQRS should only be applied to services that require its features. In most cases, this is a very small subset of services.
By having separate models for writes and reads, we have to keep them in sync, so when a command is processed, both the command and the query models are updated. In many cases, this leads to systems that are eventually consistent.
Summary and next steps
In this article, I provided a high-level overview of the CQRS architecture pattern. To learn more about the CQRS architecture pattern, watch this video, “Creating event-driven microservices: Explore event-driven architecture patterns for your microservices apps.” To learn about this pattern in a bit more detail, explore Unit 6, Unit 7, and Unit 8 in this “Reactive in Practice” tutorial series on building reactive systems.