.NET/C#
  • ASP.NET Core Overview
  • ASP.NET Core API tutorial
    • How to create ASP.NET Core C# API tutorial
      • launchSettings
      • Install swagger
        • Swagger best practices
      • Run the app
      • Fix CORS problem
      • Add AutoMapper
      • Add JWT reference
      • Add .gitignore
      • Basic structure & EF Core
      • AddSingleton
    • Loading Related Entities
  • Unit test controller logic in ASP.NET Core
    • Unit testing controllers
  • .NET Q&A
    • dotnet try
    • LINQ
      • LINQ Query Syntax
      • Lambda Expression
      • Standard Query Operators
  • .NET C# Interview questions
    • C# - .NET interview questions and answers [Part 1]
    • C# - .NET interview questions and answers [Part 2]
    • C# Interview questions [Part 3] General questions
  • C#
    • Object-Oriented Programming (Principles)
      • N-tier architecture style
      • Command and Query Responsibility Segregation (CQRS) pattern
      • Project architecture
    • C# Advanced review
      • Implicit typing
      • Generics
      • Attributes
      • Reflection
      • Delegates
      • Anonymous Methods and Lambda Expressions
      • Events
      • Ref vs Out
      • Task
        • TaskFactory Class
  • MySQL
    • MySQL Lerning
      • SELECT
      • ORDER BY
      • WHERE
      • DISTINCT
      • IN
      • BETWEEN
      • Join
      • INNER JOIN
      • LEFT JOIN
      • RIGHT JOIN
      • GROUP BY
      • Subquery
      • UNION
    • Stored Procedures
      • CREATE PROCEDURE
  • Versioning API, MongoDB and ci-cd
    • Create a web API with ASP.NET Core and MongoDB
    • REST API versioning with ASP.NET Core
    • Design a CI/CD pipeline using Azure DevOps
      • Create a CI/CD pipeline for GitHub repo using Azure DevOps Starter
  • TFS & TFCV
    • What is Team Foundation Version Control
      • Develop and share your code in TFVC using Visual Studio
      • Suspend your work and manage your shelvesets
    • Newtonsoft Json.NET
      • Serializing and Deserializing JSON
  • MS-SQL
    • Quick tutorial
      • Add new column to a table
      • LEFT/RIGHT Reverse operator
      • Dates (Transact-SQL)
      • CAST and CONVERT (Transact-SQL)
      • Types of JOIN
      • Our first Left Outer Join
      • CROSS JOIN
Powered by GitBook
On this page
  • The problem
  • Solution
  • Implementation issues and considerations

Was this helpful?

  1. C#
  2. Object-Oriented Programming (Principles)

Command and Query Responsibility Segregation (CQRS) pattern

PreviousN-tier architecture styleNextProject architecture

Last updated 4 years ago

Was this helpful?

The Command and Query Responsibility Segregation (CQRS) pattern separates read and update operations for a data store. Implementing CQRS in your application can maximize its performance, scalability, and security. The flexibility created by migrating to CQRS allows a system to better evolve over time and prevents update commands from causing merge conflicts at the domain level.

The problem

In traditional architectures, the same data model is used to query and update a database. That's simple and works well for basic CRUD operations. In more complex applications, however, this approach can become unwieldy. For example, on the read side, the application may perform many different queries, returning data transfer objects (DTOs) with different shapes. Object mapping can become complicated. On the write side, the model may implement complex validation and business logic. As a result, you can end up with an overly complex model that does too much.

Read and write workloads are often asymmetrical, with very different performance and scale requirements.

A traditional CRUD architecture
  • There is often a mismatch between the read and write representations of the data, such as additional columns or properties that must be updated correctly even though they aren't required as part of an operation.

  • Data contention can occur when operations are performed in parallel on the same set of data.

  • The traditional approach can have a negative effect on performance due to load on the data store and data access layer, and the complexity of queries required to retrieve information.

  • Managing security and permissions can become complex, because each entity is subject to both read and write operations, which might expose data in the wrong context.

Solution

CQRS separates reads and writes into different models, using commands to update data, and queries to read data.

  • Commands should be task based, rather than data centric. ("Book hotel room", not "set ReservationStatus to Reserved").

  • Commands may be placed on a queue for asynchronous processing, rather than being processed synchronously.

  • Queries never modify the database. A query returns a DTO that does not encapsulate any domain knowledge.

The models can then be isolated, as shown in the following diagram, although that's not an absolute requirement.

Having separate query and update models simplifies the design and implementation. However, one disadvantage is that CQRS code can't automatically be generated from a database schema using scaffolding mechanisms such as O/RM tools.

The read store can be a read-only replica of the write store, or the read and write stores can have a different structure altogether. Using multiple read-only replicas can increase query performance, especially in distributed scenarios where read-only replicas are located close to the application instances.

Separation of the read and write stores also allows each to be scaled appropriately to match the load. For example, read stores typically encounter a much higher load than write stores.

Benefits of CQRS include:

  • Independent scaling. CQRS allows the read and write workloads to scale independently, and may result in fewer lock contentions.

  • Optimized data schemas. The read side can use a schema that is optimized for queries, while the write side uses a schema that is optimized for updates.

  • Security. It's easier to ensure that only the right domain entities are performing writes on the data.

  • Separation of concerns. Segregating the read and write sides can result in models that are more maintainable and flexible. Most of the complex business logic goes into the write model. The read model can be relatively simple.

  • Simpler queries. By storing a materialized view in the read database, the application can avoid complex joins when querying.

Implementation issues and considerations

Some challenges of implementing this pattern include:

  • Complexity. The basic idea of CQRS is simple. But it can lead to a more complex application design, especially if they include the Event Sourcing pattern.

  • Eventual consistency. If you separate the read and write databases, the read data may be stale. The read model store must be updated to reflect changes to the write model store, and it can be difficult to detect when a user has issued a request based on stale read data.

A basic CQRS architecture

For greater isolation, you can physically separate the read data from the write data. In that case, the read database can use its own data schema that is optimized for queries. For example, it can store a of the data, in order to avoid complex joins or complex O/RM mappings. It might even use a different type of data store. For example, the write database might be relational, while the read database is a document database.

If separate read and write databases are used, they must be kept in sync. Typically this is accomplished by having the write model publish an event whenever it updates the database. For more information about using events, see . Updating the database and publishing the event must occur in a single transaction.

A CQRS architecture with separate read and write stores

Some implementations of CQRS use the . With this pattern, application state is stored as a sequence of events. Each event represents a set of changes to the data. The current state is constructed by replaying the events. In a CQRS context, one benefit of Event Sourcing is that the same events can be used to notify other components — in particular, to notify the read model. The read model uses the events to create a snapshot of the current state, which is more efficient for queries. However, Event Sourcing adds complexity to the design.

Messaging. Although CQRS does not require messaging, it's common to use messaging to process commands and publish update events. In that case, the application must handle message failures or duplicate messages. See the guidance on for dealing with commands having different priorities.

materialized view
Event-driven architecture style
Event Sourcing pattern
Priority Queues