Monday, 22 September 2025

CQRS

1️⃣ What Is CQRS?

CQRS = Command Query Responsibility Segregation

It means separating “commands” (writes) from “queries” (reads) in your application.

  • Command = an action that changes data.
    Example: PlaceOrder, UpdateProfile

  • Query = an action that only reads data.
    Example: GetOrderById, ListProducts

Instead of one big class handling both, CQRS splits them into two clear paths.


2️⃣ Why Use CQRS?

  • Better Performance: You can tune reads and writes separately.

  • Clear Code: Each side (read/write) has its own logic.

  • Scalability: You can scale read-heavy parts differently from write-heavy parts.

  • Security: Easier to control who can read vs. write.


3️⃣ How It Works (Simple Flow)

User Request | ├── Command (Write) --> Command Handler --> Database (Write model) └── Query (Read) --> Query Handler --> Database/View (Read model)
  • Command Side: Validates and changes data (e.g., insert/update).

  • Query Side: Fetches and returns data (could use a different database or optimized view).


4️⃣ Example in .NET (Step by Step)

Imagine an Order system.

A. Command

// Command object public record PlaceOrderCommand(int ProductId, int Quantity) : IRequest<int>; // Handler public class PlaceOrderHandler : IRequestHandler<PlaceOrderCommand, int> { private readonly AppDbContext _db; public PlaceOrderHandler(AppDbContext db) => _db = db; public async Task<int> Handle(PlaceOrderCommand cmd, CancellationToken ct) { var order = new Order { ProductId = cmd.ProductId, Qty = cmd.Quantity }; _db.Orders.Add(order); await _db.SaveChangesAsync(ct); return order.Id; } }

B. Query

public record GetOrderByIdQuery(int Id) : IRequest<OrderDto>; public class GetOrderByIdHandler : IRequestHandler<GetOrderByIdQuery, OrderDto> { private readonly AppDbContext _db; public GetOrderByIdHandler(AppDbContext db) => _db = db; public async Task<OrderDto> Handle(GetOrderByIdQuery q, CancellationToken ct) { return await _db.Orders .Where(o => o.Id == q.Id) .Select(o => new OrderDto(o.Id, o.ProductId, o.Qty)) .FirstOrDefaultAsync(ct); } }
  • Here we use MediatR (popular .NET library) to route commands and queries.


5️⃣ When to Use CQRS

✅ Complex domains with many reads and writes
✅ Applications needing high scalability or performance tuning
✅ Systems where read and write models are very different (e.g., reporting dashboards)

🚫 Overkill for small CRUD apps—it adds extra classes and setup.


6️⃣ Advanced Option: Event Sourcing

Sometimes paired with CQRS:

  • Instead of saving only the current state, you store every event (e.g., OrderPlaced, OrderShipped).

  • Read side rebuilds views from these events.


7️⃣ Quick Summary

  • Command: change data

  • Query: read data

  • Responsibility: keep them separate

  • Segregation: independent models/handlers

In short:
CQRS helps keep your write logic and read logic clean, fast, and easy to scale. In .NET, you can implement it using MediatR, EF Core, and separate handlers for commands and queries.