Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / CQRS

Overview of CQRS

4.21/5 (8 votes)
20 Sep 2023CPOL4 min read 5.2K  
Understanding CQRS pattern at a high level
When developing applications with different read and write requirements, CQRS might be the appropriate pattern in many scenarios.

Introduction

Applications usually have the same Data Models and databases for read and write operations. And this is ideal for most of the applications. However in many scenarios, applications have different read and write requirements.

What is CQRS

When designing any application, we create domain models which correspond to the entities in our problem domain. These are usually classes which also contain business and validation logic. Also, in the case of a relational database, these may correspond to the database entities.

Traditionally, applications use the same data models for both read and write operations. This is a good approach as it does not create any extra complexity since we are using single database and corresponding models in our application.

But there are many scenarios where this approach may not be ideal. For example, when number of read operations is significantly different than the number of write operations. For example, if the application has large volumes of read operations such as in the case of Flight booking API.

This might work well in most scenarios, but depending on the requirement may have the following disadvantages:

If the read requirements of the application are different from the write requirements:

  • If there is significant difference in logic used in DTO and Model objects used for update. For example, if the update operations have complex validation requirements or if the DTOs needs to fetch data from various different data sources.
  • If application's read operations might need significant scaling compared to updated operations. This could be in scenarios where there are massive number of requests for viewing compared to updating the models.

Role of Command and Query In CQRS

Lets say we have an Ecommerce application. This application allows the users to view and as well as purchase deterrent products. Now one way to design such an application is by using the same Data Model for both read and write operations( Create , Update and Delete).

Image 1

Consider this scenario for this application. It is working perfectly and meet the business requirements. Now after some time lot of users are interested in browsing the products as compared to actually purchasing the products. Another problem, since we are using the same Data Models for both read and write operations we are not using our model optimally. For example we need to implement security in our model even when it is just being used for viewing purpose.

So the two important problems we face now are:

  • We can not scale the read and write parts of our application selectively.
  • We need to implement same validations and model optimizations for both read and write operation.

CQRS is the ideal solution for such problems. We can redesign such an application by segregating application responsibilities into Read and Write parts.

CQRS stands for Command Query Responsibility Segregation and it is used  for segregating application into Read side and Write side.

  • C stands for Query and consists of operations which update the state of our application. These include or Create, Update and Delete operations.
  • Q stands for Query and consists of operations which returns the state of our application. This includes Read operations.

One of the main advantages of this pattern is that since it separates responsibilities between Read and Update operations, it makes the application more flexible.

With this understanding we can redesign our application by segregating application responsibilities into Read and Write sides.

Image 2

We have now solved two important problems above by implementing CQRS:

  • We can scale our Read and Write Models independently. This varies based on the requirements for read and write models.
  • We can optimize the Read and Write models. Read model can be optimized to use objects such as materialized view for faster data retrieval instead of using multiple table joins. Similarly write models can be optimized separately. This results in more flexible design.

Implementation considerations

For implementing this pattern, we need to have separate domain models for Command and Query.

Clients will invoke the Command or the Query that is executed by the command handler. So, command handler or query handler will accept the command or query object as a parameter.

C#
public interface ICommandHandler
{
   bool HandleCommand(SampleComamnd);

}
public interface IQueryHandler
{
    bool HandleQuery(SampleQuery);
}

We don't necessarily use different databases for commands and queries .It depends on the requirements such as if we need to physically keep read and write data separate using separate databases. If we are using different databases then we need to consider how to keep the read database in sync with the write database. Event sourcing is one way we can achieve this.

So important challenge in implementing CQRS is to manage consistency when it comes to syncing read and write databases.

For understanding this, we can consider two basic consistency models:

  • Strong Consistency: Updates are available in all the database replicas as soon as they are complete.
  • Eventual Consistency: There is some delay for the changes to get propagated to the replicas.

There is always some trade off between scalability and consistency. While designing a system using CQRS, we get high scalability and flexibility.

The updates which are done on the write database will be propagated to the read database. There could be some delay in doing this. But eventually, read database will be updated with these changes.

History

  • 20th September, 2023: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)