Understanding CQS/CQRS in C#

Introduction:
Modern software development emphasizes the need for building applications that are scalable, maintainable, and performant. One way to achieve these goals is by adopting architectural patterns such as CQS and CQRS. In this article, we will dive deep into these patterns and understand their benefits.

What is Command-Query Separation (CQS)?
Command-Query Separation is a principle that states that methods should be categorized as either commands (mutators) or queries (accessors) but not both. It promotes the separation of concerns between operations that modify the system’s state and operations that read the system’s state.

Code Example:

public class OrderService
{
   private readonly IOrderRepository _orderRepository;

   public OrderService(IOrderRepository orderRepository)
   {
      _orderRepository = orderRepository;
   }

   // Command method to place an order
   public void PlaceOrder(Order order)
   {
      // Perform order placement validation, business logic, etc.
      // ...

      _orderRepository.Save(order);
   }

   // Query method to get an order by ID
   public Order GetOrderById(int orderId)
   {
      return _orderRepository.GetById(orderId);
   }
}

What is Command Query Responsibility Segregation (CQRS)?
Command Query Responsibility Segregation is an architectural pattern that extends the principles of CQS by dividing the application into separate read and write models. It separates the responsibility for handling commands that modify the system’s state from the responsibility for handling queries that retrieve data from the system.

Code Example:

public class OrderCommandHandler
{
   private readonly IOrderRepository _orderRepository;

   public OrderCommandHandler(IOrderRepository orderRepository)
   {
      _orderRepository = orderRepository;
   }

   public void HandlePlaceOrderCommand(PlaceOrderCommand command)
   {
      // Perform order placement validation, business logic, etc.
      // ...

      var order = new Order(command.CustomerId, command.ProductId, command.Quantity);
      _orderRepository.Save(order);
   }
}

public class OrderQueryHandler
{
   private readonly IOrderRepository _orderRepository;

   public OrderQueryHandler(IOrderRepository orderRepository)
   {
      _orderRepository = orderRepository;
   }

   public OrderDto HandleGetOrderByIdQuery(GetOrderByIdQuery query)
   {
      var order = _orderRepository.GetById(query.OrderId);
      return new OrderDto(order);
   }
}

Conclusion:
CQS and CQRS are powerful patterns that can fundamentally change the way we design and build our applications. By separating commands from queries, we can achieve better decoupling, scalability, and maintainability. CQRS takes this concept further by segregating the read and write responsibilities, allowing for even greater performance and scalability optimizations.

By understanding and implementing these patterns in our C# applications, we can build robust and scalable systems that can handle complex business requirements efficiently.

References: