CQRS

Separates the read model from the write model to optimize each independently.

Context

Your reads and writes have very distinct shapes and requirements: queries need denormalization and caching; commands must validate complex invariants.

Problem

A single model trying to serve both optimizes for neither.

Solution

Two stacks: Commands (mutate, return only success/failure) and Queries (read, return DTOs adapted to the view).

#Example in C# — with MediatR

// Command (write)
public record PlaceOrderCommand(Guid CustomerId, IReadOnlyList<LineItem> Items) : IRequest<Guid>;

public class PlaceOrderHandler : IRequestHandler<PlaceOrderCommand, Guid>
{
    private readonly IOrderRepository _repo;
    public PlaceOrderHandler(IOrderRepository r) => _repo = r;

    public async Task<Guid> Handle(PlaceOrderCommand cmd, CancellationToken ct)
    {
        var order = Order.Create(cmd.CustomerId, cmd.Items);
        _repo.Add(order);
        return order.Id;
    }
}

// Query (read)
public record GetOrderSummaryQuery(Guid Id) : IRequest<OrderSummaryDto>;

public class GetOrderSummaryHandler : IRequestHandler<GetOrderSummaryQuery, OrderSummaryDto>
{
    private readonly DbConnection _db;
    public Task<OrderSummaryDto> Handle(GetOrderSummaryQuery q, CancellationToken ct) =>
        _db.QueryFirstAsync<OrderSummaryDto>("SELECT id, total, status FROM order_summary WHERE id=@id", q);
}
When NOT to use it
  • When the model is simple (CRUD): separation adds ceremony without gain.
  • When the team is not comfortable with eventual consistency between read/write models.
Tradeoffs
Pro Con
Independent optimization Two models to maintain
Reads scale independently Synchronization (eventual consistency) between models

#extra #ddd #scaling