CQRS
Understanding Command-Query Responsibility Segregation
Last updated
Understanding Command-Query Responsibility Segregation
Last updated
Software developers commonly face a design challenge because of the way users view data. The data of effective user interface views tends to be in quantities, shapes, and sizes that are different from the way users modify data. Using a traditional domain model that provides both mutation and query operations together results in the following problems, because the data users view cannot be retrieved as optimally as is generally possible for mutation.
The system state that the user wants to view tends to be rich and cuts across at least several logical entity/aggregate types. The state that the user wants to mutate tends to be concentrated on one one aggregates or a few entities. The strengths of CQRS are in enabling developers to design for state mutation separately from state queries. The state mutation operations are optimized for creates and updates. The operations and data used for querying are optimized for how the user tends to view the system state. Both of these sets of operations and states can be designed and scaled separately. In brief, the stated software design challenges can be addressed and related problems overcome with the use of CQRS.
One of the primary misunderstandings even among those who claim to know CQRS well, is thinking that CQRS is a system-level or top-level architecture, as in an architectural style. It’s possible that some think this simply because they don’t understand what constitutes a top-level architecture, or architectural style. An architectural style describes the pattern that is followed by an entire application or service, and not just some small portion of the the overall application or service. For example, there are Layers architecture and Ports and Adapters architecture.
Ports and Adapters is an architecture that has an impact on the way an application or service as a whole works. When the Ports and Adapters architecture is used, everything within the application or service reflects some well-defined part of the definition of Ports and Adapters. Although Ports and Adapters highlights the adapters for incoming stimulus, the architecture also defines the inside with the application interfaces. Additionally, the application inside may be further divided into application services and a domain model.
CQRS is not intended to be and architectural style since it doesn’t by definition have an impact on the overall application or service. Instead, CQRS is an architecture pattern; one that has some substantial influence on the inner workings of the application or service, but not every part of it. With that in mind, consider what CQRS actually means.
The CQRS architecture pattern is based on the CQS software principle defined by Bertrand Meyer, creator of the Eiffel programming language. To understand CQRS it helps to first understand CQS. It’s fitting then to provide a few definitions.
CQS, or Command-Query Separation states that a software interface abstraction is designed with two types of methods. One method type is known as a Command method, and the other method type is known as a Query method. A Command method modifies or mutates the state underneath the interface, but does not answer any portion of that state. A Query method answers the current state beneath the interface, but must not modify that state before answering it. In particular note that CQS mandates the definition of the methods of a single interface abstraction. In the literature CQS is not considered a pattern, but a principle. I don’t want to split hairs over this, because you might argue that CQS is a design pattern, but it’s difficult to find official definitions to support that argument.
Although there is more detail to the Product
interface, what is shown is enough to make the essential point about CQRS. This interface combines both commands and queries. The changeSku()
and rename()
are command methods, which are used to modify the sku
and name
, respectively. The querySku()
and queryName()
are used to read the current state of the Product sku
and name
, respectively.
This seems to be designed appropriately. The problem is that when the user needs to view the Product
they also need to view the Pricing
, Availability
, SimilarProducts
, ProductReviews
, and possibly other information that is loosely associated with a given Product
. Considering that there is a minimum of five objects that must be retrieved for that user view, it will certainly be quite difficult and inefficient to query each piece of data separately.
With motivations to optimize for the above use case, we decide to design our Command Model and our Query Model separately. Thus, the Product
in the Command Model is refactored as follows, with command operations only.
CQRS, or Command-Query Responsibility Segregation, is similar to CQS because it focuses on separating Command methods and Query methods, but it does so to an even greater degree. This is a good time to highlight the terms responsibility and segregation. The term responsibility conveys the idea that Commands are not only a type of method, but a type of software responsibility. The same goes for Queries. The term segregation emphasizes that the two responsibilities, Commands and Queries, are segregated from each other using separate interfaces. Thus, where there is one Product
interface in the above example, there are now two interfaces.
To optimize for the above use case, we decide to design the Command Model and the Query Model separately. Thus, the Product
in the Command Model is refactored as follows, with command operations only.
The Query Model is even in a separate module/package.
It seems important to note that the above segregated interfaces emphasize why CQRS is not an architectural style, but an architecture pattern. Since Product
is likely not the only business concept in the application or service, there could be several other business model concepts defined. These are concepts that are more closely associated with the interior of the application or service, generally know as the domain model.
It's not an oversimplification to say that CQRS is just that simple.
Considering a traditional domain model, there is just one model that defines the application’s or service’s state. Defining our business concepts using the CQS principle, there is still just one root object constituting a Product
.
When using CQRS it is typical to use not one, but two, models, those being a Command Model and a Query Model. This is part of the segregation, but in addition to the interfaces there are the software models of the segregated states. There are the segregated states used by the Commands, and segregated states used by the Queries. This is at least a logically segregation because there is nothing to prevent the implementation from persisting the Command states and Query states together. Still, it is easier to reason about two state models rather than one.
When methods on the Product
interface are executed, the Command Model state is mutated. When methods on the ProductQueries
interface are executed, the Query Model state is queried. That makes sense, except for how the Query Model state comes about. That is illustrated by the following diagram.
The Query Model is built up by projecting on to it the changes made to the Command Model. As the user submits Command operations, the outcome of the Command Model mutations are used by software components to projection appropriate updates onto the Query Model. The user views the system state by way of the use-case-optimized Query Model.
In the above diagram, the Command Model mutations are captured by Domain Events, which are also used to project updates onto the Query Model. It is not necessary to use Domain Events for this. The entire state of a Command Model object that is mutated may be projected instead. If this approach is used, it’s helpful to indicate to the projection components what the actual Command operation was that caused the state to mutate. This enables the projection components to have both a rich set of data to project, but with full knowledge of the actual state that changed.