blog

home / developersection / blogs / basic implementation of cqrs in c#

Basic Implementation of CQRS in C#

Basic Implementation of CQRS in C#

Anubhav Kumar 993 23-Jan-2025

The Command Query Responsibility Segregation (CQRS) pattern is a design principle that separates the responsibility for handling queries (read operations) and commands (write operations). This separation allows developers to optimize the read and write parts of the system independently, resulting in cleaner code, better scalability, and easier testing.

 

Key Concepts of CQRS

  1. Command: Represents an operation that changes the state of the system (e.g., creating, updating, or deleting data).
  2. Query: Represents an operation that retrieves data without modifying the state.
  3. Segregation: Commands and queries are implemented in separate models, classes, or layers.
  4. Independence: Read and write models can use different technologies, databases, or schemas for optimization.

 

Basic Implementation of CQRS in C#

 

Basic Implementation of CQRS in C#

 

Define Commands

Commands represent actions that change the application's state.

public class CreateOrderCommand
{
    public int OrderId { get; set; }
    public string ProductName { get; set; }
    public int Quantity { get; set; }
}

 

Command Handler

Handles the logic for processing a command.

public interface ICommandHandler<TCommand>
{
    void Handle(TCommand command);
}

public class CreateOrderCommandHandler : ICommandHandler<CreateOrderCommand>
{
    public void Handle(CreateOrderCommand command)
    {
        // Logic to save the order to the database
        Console.WriteLine($"Order {command.OrderId} created for {command.ProductName}.");
    }
}

 

Define Queries

Queries represent operations that fetch data without modifying it.

public class GetOrderQuery
{
    public int OrderId { get; set; }
}

public class OrderDto
{
    public int OrderId { get; set; }
    public string ProductName { get; set; }
    public int Quantity { get; set; }
}

 

Query Handler

Handles the logic for processing a query.

public interface IQueryHandler<TQuery, TResult>
{
    TResult Handle(TQuery query);
}

public class GetOrderQueryHandler : IQueryHandler<GetOrderQuery, OrderDto>
{
    public OrderDto Handle(GetOrderQuery query)
    {
        // Simulated data retrieval
        return new OrderDto
        {
            OrderId = query.OrderId,
            ProductName = "Sample Product",
            Quantity = 1
        };
    }
}

 

Mediator for Dispatching

A mediator centralizes the logic for dispatching commands and queries to their respective handlers.

 

Basic Implementation of CQRS in C#

 

public class Mediator
{
    private readonly IServiceProvider _serviceProvider;

    public Mediator(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public void Send<TCommand>(TCommand command)
    {
        var handler = _serviceProvider.GetService(typeof(ICommandHandler<TCommand>)) as ICommandHandler<TCommand>;
        handler?.Handle(command);
    }

    public TResult Query<TQuery, TResult>(TQuery query)
    {
        var handler = _serviceProvider.GetService(typeof(IQueryHandler<TQuery, TResult>)) as IQueryHandler<TQuery, TResult>;
        return handler != null ? handler.Handle(query) : default;
    }
}

 

Register and Use

Set up the dependency injection and use the mediator to send commands and queries.

var services = new ServiceCollection();

// Register handlers
services.AddTransient<ICommandHandler<CreateOrderCommand>, CreateOrderCommandHandler>();
services.AddTransient<IQueryHandler<GetOrderQuery, OrderDto>, GetOrderQueryHandler>();

// Register mediator
services.AddSingleton<Mediator>();

var serviceProvider = services.BuildServiceProvider();
var mediator = serviceProvider.GetRequiredService<Mediator>();

// Use mediator
mediator.Send(new CreateOrderCommand { OrderId = 1, ProductName = "Laptop", Quantity = 2 });

var order = mediator.Query<GetOrderQuery, OrderDto>(new GetOrderQuery { OrderId = 1 });
Console.WriteLine($"Order Retrieved: {order.ProductName}, Quantity: {order.Quantity}");

 

Advantages of CQRS

  1. Separation of Concerns: Clear distinction between read and write logic.
  2. Scalability: Enables optimization of read and write operations independently.
  3. Flexibility: Allows the use of different technologies or data stores for read and write models.
  4. Easier Testing: Each command and query handler can be unit tested in isolation.

 

When to Use CQRS

  • Complex systems with heavy read/write operations.
  • Applications requiring different scaling for reads and writes.
  • Systems with highly complex business rules for data modification.

 

When Not to Use CQRS

  • Simple applications with straightforward data access needs.
  • Systems where the added complexity outweighs the benefits.

 

CQRS can be combined with patterns like Event Sourcing for further benefits, but it should be implemented where justified to avoid over-engineering.


c# c# 
Updated 23-Jan-2025
Anubhav Kumar

Student

The Anubhav portal was launched in March 2015 at the behest of the Hon'ble Prime Minister for retiring government officials to leave a record of their experiences while in Govt service .

Leave Comment

Comments

Liked By