0
0
CsharpConceptIntermediate · 3 min read

CQRS Pattern in C#: What It Is and How It Works

The CQRS (Command Query Responsibility Segregation) pattern in C# separates the methods that change data (commands) from those that read data (queries). This helps improve performance, scalability, and maintainability by handling reads and writes differently.
⚙️

How It Works

Imagine a busy restaurant kitchen where one chef only takes orders (commands) and another chef only prepares meals based on those orders (queries). In the CQRS pattern, the system splits responsibilities so that commands handle changes to data, like adding or updating information, while queries handle reading data without changing it.

This separation allows each side to be optimized independently. For example, the read side can use a fast, simple database structure for quick data retrieval, while the write side can focus on validating and processing changes safely. This makes the system easier to manage and scale, especially when many users read data but fewer change it.

💻

Example

This example shows a simple C# implementation of CQRS with separate classes for commands and queries.

csharp
using System;
using System.Collections.Generic;

// Command to add a new product
public class AddProductCommand
{
    public string Name { get; set; }
    public decimal Price { get; set; }
}

// Query to get all products
public class GetProductsQuery { }

// Product entity
public class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }
}

// Handler for commands
public class CommandHandler
{
    private readonly List<Product> _products;

    public CommandHandler(List<Product> products)
    {
        _products = products;
    }

    public void Handle(AddProductCommand command)
    {
        var product = new Product { Name = command.Name, Price = command.Price };
        _products.Add(product);
        Console.WriteLine($"Product '{product.Name}' added.");
    }
}

// Handler for queries
public class QueryHandler
{
    private readonly List<Product> _products;

    public QueryHandler(List<Product> products)
    {
        _products = products;
    }

    public IEnumerable<Product> Handle(GetProductsQuery query)
    {
        return _products;
    }
}

public class Program
{
    public static void Main()
    {
        var products = new List<Product>();
        var commandHandler = new CommandHandler(products);
        var queryHandler = new QueryHandler(products);

        // Add products using command
        commandHandler.Handle(new AddProductCommand { Name = "Apple", Price = 0.5m });
        commandHandler.Handle(new AddProductCommand { Name = "Banana", Price = 0.3m });

        // Get products using query
        var allProducts = queryHandler.Handle(new GetProductsQuery());

        Console.WriteLine("Products list:");
        foreach (var product in allProducts)
        {
            Console.WriteLine($"- {product.Name}: ${product.Price}");
        }
    }
}
Output
Product 'Apple' added. Product 'Banana' added. Products list: - Apple: $0.5 - Banana: $0.3
🎯

When to Use

Use CQRS when your application has complex business logic and different needs for reading and writing data. It is especially helpful when many users read data often but only a few make changes, like in e-commerce sites or banking systems.

This pattern improves performance by allowing you to optimize read and write operations separately. It also helps keep your code clean and easier to maintain by clearly separating responsibilities.

Key Points

  • CQRS splits commands (writes) and queries (reads) into separate models.
  • This separation allows independent optimization and scaling.
  • It improves code clarity by separating responsibilities.
  • Best for systems with high read-to-write ratios or complex business rules.

Key Takeaways

CQRS separates data-changing commands from data-reading queries for better design.
It allows optimizing read and write operations independently.
Use CQRS in applications with complex logic or many reads and fewer writes.
This pattern improves scalability, performance, and code maintainability.