Tips and Tricks #182: Use CQRS for Complex Domain Logic

Separate read and write operations for better scalability and simpler code.

Code Snippet

// Commands (Write side)
public record CreateOrderCommand(string UserId, List Items);

public class CreateOrderHandler
{
    private readonly IOrderRepository _repo;
    
    public async Task Handle(CreateOrderCommand cmd)
    {
        var order = Order.Create(cmd.UserId, cmd.Items);
        await _repo.SaveAsync(order);
        return order.Id;
    }
}

// Queries (Read side)
public record GetOrderSummaryQuery(string OrderId);

public class GetOrderSummaryHandler
{
    private readonly IReadOnlyDbContext _db;
    
    public async Task Handle(GetOrderSummaryQuery query)
    {
        return await _db.OrderSummaries
            .Where(o => o.Id == query.OrderId)
            .FirstOrDefaultAsync();
    }
}

Why This Helps

  • Optimized read models for queries
  • Simpler command handlers with single responsibility
  • Independent scaling of read/write workloads

How to Test

  • Test commands and queries independently
  • Verify read model consistency

When to Use

Complex domains, high-read workloads, event-sourced systems.

Performance/Security Notes

Start simple. Full CQRS with separate databases adds complexity. Use when justified.

References


Try this tip in your next project and share your results in the comments!


Discover more from Byte Architect

Subscribe to get the latest posts sent to your email.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.