CQRS Architecture Pattern | A Holistic View
CQRS is an acronym for Command and Query Responsibility Segregation, an application architecture pattern that segregates Command (write operations) and Query (read operations) on a data store and opens up opportunities to enhance the application performance and makes the application more scalable. At the same time, the segregation facilitates in controlled access to the data store, hence better security for the application.
An important thing to understand here is that CQRS is an architecture pattern that doesn’t infer working with microservices, domain-driven architectures, or event-driven architectures.
Let’s first see what exactly are the Commands and Queries. A Command is an operation that changes the state of an application, and a Query is an operation that reads the state of the application. In a traditional application, the data is represented as a single model and the model handles all the CRUD operations (Create, Read, Update & Delete). With application architectures getting more complex day by day, the model can become really sophisticated and unmanageable.
A traditional approach that people generally take to interact with a software application is to treat it as a CRUD datastore. The application uses models which are common for both read and write operations. Having the same model for Read and Write operations increases the complexity of the architecture with more complex logic added. Beyond a certain point, it could become very difficult to maintain and optimise. Below is a visual presentation of a traditional approach –
The CQRS Architecture Pattern splits the model definition into two different models, where the Command Model is responsible for writing to the datastore, and on the datastore, there could be Materialized Views that form the Read Model to serve the queries, or it could be a separate datastore as well, probably a replica. The Command model (write operation) is responsible for validations, commands, complex domain logic, and data persistence to the datastore. However, the Query Model (read operation) is responsible only for reading data from, which simplifies the architecture. Below is a visual presentation of a simplified approach based on the CQRS Architecture Pattern –
Advantages of the CQRS Architecture Pattern
Alignment to Single Responsibility Principle
The architecture pattern conforms to the very first principle of the SOLID Principles — the Single Responsibility Principle. It segregates the read and write Data Models and resulting in segregated responsibilities.
Alignment to Separation of Concerns
By definition, the CQRS architecture pattern segregates the Read Model and Write Model so that validations, complex business logic related to the Write Model remain separate from the Read Model.
One of the major advantages of having the CQRS architecture pattern in place is Application Scalability. Both Read Model and Write model can be scaled independently. For instance, if an application has more reads than writes, then Read Model can be scaled up separately to have a better performance.
The CQRS architecture pattern facilitates controlled access to the data store, and with segregated Read and Write Models, the security aspect can be handled more easily. Managing separate permissions for specific models becomes much easier in this architecture pattern.
Shortfalls of the CQRS Architecture Pattern
Although the CQRS Architecture Pattern is quite robust and pretty simple to have as part of application architecture, it is important to understand the shortfalls of the pattern so that the right decision is taken considering all the aspects. Below are a few of such points to consider before going ahead with the architecture pattern –
- Code Duplication — The moment we segregate the Read and Write Models, duplication of the code becomes inevitable.
- Replication Lag/Eventual Consistency — In the case of two separate datastores supporting Read and Write Models, the Read datastore needs to be updated in real-time, which may result in eventual replication lag or eventual consistency.
- Adding Complexity to CRUD Operations — At times when the application is rather simple in nature and has simple CRUD operations to perform, in such cases having the CQRS architecture pattern can become an overhead for the development team.
Microservices and the CQRS Architecture Pattern
In the Microservices world, the CQRS Architecture Pattern can be accomplished by grouping Command (Write) operations in one service layer and Query (Read) operations in another service layer or a separate service altogether, irrespective of the technology or patterns used to develop the individual services. Each of the service layers can have its own data model (maybe a different datastore altogether).
For datastore segregation, a view datastore can be defined, which can be a read-only replica. Further, depending on the nature of the application, the view datastore can be brought in sync by either applying mirroring strategy or by subscribing to the Domain Events published by the services that own the data.
Event-Driven Architecture with CQRS
Event-driven architecture (EDA) is a software architecture paradigm promoting the production, detection, consumption of, and reaction to events. The fundamental objects in an Event-Driven Architecture are Events, and an Event can be defined as a significant change in the state of the domain model at a specific point in time.
Although the CQRS can be used without Event-Driven Architecture, both do complement each other. That’s the reason it is very common for the systems that use CQRS to leverage the use of events. To ensure that the changes in the state of the system and the Events publishing are in sync, the publishing of the event is done transactionally together with the processing of the command and the changes to its database. Below is a simplified representation of the CQRS Architecture Pattern based on Event-Driven Design –
Summarising the thoughts
The CQRS Architecture pattern can be very beneficial for considerably complex applications where the data consumption is high. Along with the aforementioned benefits, the pattern facilitates additional benefits like parallel development and easy maintenance of the applications. For instance, a part of the team can work on the Command Model and the other team members can work on the Query Model.
Generally, the CQRS Architecture pattern is coupled with Domain-Driven Design, to ensure that different domains don’t get intermingled. This further helps to avoid having unnecessary complexity of code during the development and conforms to the SOLID Principles.