In this article, I am going to introduce CQS and CQRS patterns and present how easy it is to apply CQRS pattern when writing applications in .NET Core using MediatR library.
1. What are CQS and CQRS?
CQS (Command Query Separation) is a concept presented in 1986 by Bertrand Meyer which stands that every system method can be classified as a command or query.
– Command – method changing application state and not returning value
– Query – method returning value, but not changing application state
CQRS (Command Query Responsibility Segregation) is a concept presented by Greg Young and Udi Dahan, that is an evolution of CQS (CQRS is using the CQS concept). CQRS is about separate models for write and read operations. It is recommended to have a separate database for reading and writing, but it is not required. On the figure 1, there is an example schema presented of an application that uses the CQRS pattern.
2. Data synchronization between database for reading and writing
Data synchronization between a database for reading and writing operations is one of the main problems that we have to face when using CQRS pattern in applications. First of all it is worth to mention CAP theorem, which states that it is impossible for distributed data store to provide more than two out of three:
– Consistency – every read operation returns the most recent write or error,
– Availability – every request receives positive response, but it is not guaranteed that the response contains the most recent write,
– Partition tolerance – system continues to work despite any number of communication breakdowns between nodes in system.
When using CQRS pattern we have to be aware of mentioned theorem, because it is not solving CAP issues. However CQRS allows us to decide independently what is important for us on the write and read side. For example we can decide for the system, to be an ACID (Atomic, Consistent, Isolated, Durable) compliant on the write side and to be a BASE (Basic Availability, Soft-State, Eventually Consistent) compliant on the read side.
3. What is MediatR?
MediatR is one of the most popular NuGet packages (https://github.com/jbogard/MediatR). MediatR is helping writing applications using Mediator design pattern and CQRS.
4. What are the advantages and disadvantages of using CQRS?
CQRS, as any pattern, solves specific problems and has both advantages and disadvantages which are presented below.
– Thanks to segregation, we will no longer have complex model classes. We would have a model for every data operation and it gives us a lot of flexibility.
– Separating writing activity from reading allows us to select appropriate database technology for the task. We can use SQL database for writing and non-SQL database for reading. Second option is to use the same type, for example SQL database and optimize one of them for reading and one of them for writing. It is possible to do this in MSSQL. Database optimized for reading would have more indexes and the data redundance would be high. As the writing operation has to be fast, a structure without the overhead of indexes should be used.
– When using a separate database for reading and writing activities, we can scale them independently of each other.
– You end up with a lot more code lines than when you do not use CQRS and that is why it is not recommended to use this pattern for simple CRUD applications.
– It makes the whole system complex. – When using a separate database for reading and writing, a data consistency in databases has to be handled.
5. Example implementation
In this article, I am presenting a simple CQRS implementation. In this solution, the same database is used for reading and writing operations, but it can be easily modified to separate it.
First of all, a new project has been created and the NuGet packages have been installed.
nuget install MediatR
nuget install MediatR.Extensions.Microsoft.DependencyInjection
nuget install Microsoft.EntityFrameworkCore
nuget install Microsoft.EntityFrameworkCore.Design
nuget install Microsoft.EntityFrameworkCore.SqlServer
The created solution consists of 5 projects as presented in the Figure 2.
In CQRS. Application project some interfaces act as an abstraction over command and query. Those interfaces are just simple empty interfaces that are called “marker interfaces” (Fig. 3)
In this project, there are also empty handlers interfaces wrapping IRequestHandler interface from the MediatR library. They are responsible for handling corresponding queries and handlers (Fig. 4).
To make it work, MediatR has to be registered in a Startup class. Thanks to that every Command and Request placed in the CQRS.Infrastructure project is registered (Fig. 5).
In this example a database containing a table with products was created using Entity Framework Core. The product model is presented in the Figure 6.
5.1 Creating command
When we defined IQuery, ICommand and corresponding handlers we are able to start creating commands and queries in our system that would allow to manage products. One of the created commands in our system is a command that is responsible for adding products to the database (Fig. 6). This command is implementing the ICommand interface and contains data that is passed to the command handler.
When a command is created we can create a AddProductCommandHandler class (Fig. 7) that would handle AddProductCommand. This class has to implement the ICommandHandler interface. The command class that is handled, has to be passed as a generic parameter to ICommandHandler.
5.2 Creating query
In the previous step, we created a command and command handler for adding products to the database. To return data from our system to the user, we have to create a query.
By analogy to the command, we create a class GetProductsQuery (Fig. 8) that implements IQuery interface and passes into this interface a generic parameter that represents a return type.
To handle this query, we are creating a GetProductsQueryHandler (Fig. 9).
This class is implementing IQueryHandler interface with two parameters:
a) Class representing query that has to be handled – in our case GetProductsQuery
b) Return type – IEnumerable<ProductDTO>
As we created command and query and handled those actions we can use it in the controller (Fig. 10) and test if everything is working correctly.
As shown in the example, it is easy to implement the CQRS pattern using the MediatR library in .NET Core. Taking some time, it is also possible to modify this solution to use a separate database for reading and writing operations, but the synchronization between databases has to be handled by the developer. As shown also in this example, a simple CRUD application written using CQRS pattern consist of a lot of classes and lines of code and that is why it is not recommended to use this pattern in applications that are CRUD applications.
In our company we had a project where the CQRS pattern was used. When I and other people joined this team, it was easy for us, as new team members, to easily understand what is happening in the application code and write new functionalities and adjust newly added code to this pattern.