What is CQS?
CQS is a design pattern, and the acronym stands for Command Query Separation. (CQS) is the core concept the defines two types of operations handled in a system: a command that executes a task, a query that returns information, and there should never be one function that does both of these jobs.
The term was defined by Bertrand Meyer in his Book 'Object-oriented Software Construction' (1988, Prentice-Hall). He created it as part of his work on the Eiffel programming language.
CQRS takes the defining principle of CQS and extends it to specific objects within a system, one retrieving data and one modifying data. CQRS is the broader architectural pattern, and CQS is the general principle of behavior.
What is CQRS?
Command and Query Responsibility Segregation (CQRS) is the segregation of the responsibilities of the commands and queries in a system
CQRS was defined by Greg Young:
Command and Query Responsibility Segregation uses the same definition of Commands and Queries that Meyer used and maintains the viewpoint that they should be pure. The fundamental difference is that in CQRS objects are split into two objects, one containing the Commands one containing the Queries.
"Objects' in the original definition are not connected to the storage, but to the handlers. We're creating different pipelines for different business behaviors, not separate storage.
Structure of CQRS
- Command. A command is a request for the system to perform an action that changes the state of the system. Command are imperatives; An example of a command is 'RegisterUser'. In the bounded context, commands originate either from the UI as a result of a user initiating a request.
- Command Bus The command bus matched commands to handlers. This matching can be done automatically by some kind of naming convention. Another option is to register the relationships on the command bus.
When a command is dispatched, the bus locates the handler and calls the handle method.
- Command Handlers contain actual business logic which validates and processes data received in commands. Command handlers are responsible for the generation and propagation of domain events to the Event Bus.
- Event. An event represents a fact that took place in the domain. They are the source of truth; your current state is derived from the events. They are immutable and represent the business facts.
- Events are referred to in the past tense and represent the specific business fact. For example, 'UserRegistered' show the state of the user has definitely changed, rather than just come into being.
- An event usually contains unique metadata such as the timestamp of the event, the unique identifier of the subject, etc. The data within the event will be used in the write model to populate the state and make decisions as well as populate read models.
- Event Bus is an open-source library using the publisher/subscriber pattern for loose coupling. EventBus enables central communication to decoupled classes with just a few lines of code – simplifying the code, removing dependencies, and speeding up app development.
- Event Handlers can update a read model, but might also perform other tasks. An example I've encountered was sending out an email when certain events had happened.
- Queries are objects, which represent the actual state of application available for a user. Getting data for UI should be done through these objects.
- A query is a request for information, It returns a result but does not change the state.
Advantages or Pros using CQRS pattern
- Clear boundaries the system behavior or proper implementation Single Responsibility Principle.
- Loose coupling, the logic is cohesive and less interrelated, making it easier to create modular, maintainable applications.
- Independent Scaling and Separation of Concern:
- Separating write activity from reading activities allows you to use the best database technology for the task at hand, for example, you can use Relational DB for writing and Document DB, ElasticSearch or etc for reading so that we can scale the read and write database independently of each other.
- DB normalization and denormalization:
- The write database can be normalized to the 3rd Normal Form to make it efficient.
- The read database can be denormalized the data to suit specific queries and need not perform complex operations like JOIN tables to return the required data.
Disadvantages of the CQRS pattern
Although the CQRS pattern is exciting and easy to understand, in real scenarios it can be painful if not used correctly. Below are some of the downsides of this pattern:
- It adds unnecessary complexity if applications have simple CRUD operations, with can be achieved by traditional architectural styles.
- As we require separate models for reading and writing, code duplication is inevitable.
- In the case of two separate databases for reading & writing database needs to update the read database that could result in Eventually Consistent Views.
When to use the CQRS Pattern
Below are some scenarios where the CQRS fits perfectly:
- Large projects where high performance is required and independent scalability is required.
- In the applications where business logic is complex. In such a case, you can separate your read and write to make it more simple.
- If you want parallel development where one team can work on the read model and the other team works on write models.