Yaseer Arafat

Mastering CQRS with MediatR in .NET

“Separation of concerns isn’t just a principle — it’s a power tool.”

In this guide, you’ll see how to implement CQRS using MediatR to build clean, scalable architecture for modern .NET applications.


Why CQRS?

CQRS (Command Query Responsibility Segregation) helps you:


Project Structure

/Features
   /Products
     CreateProductCommand.cs
     CreateProductHandler.cs
     GetProductByIdQuery.cs
     GetProductByIdHandler.cs

1. Define the Command

// CreateProductCommand.cs
public record CreateProductCommand(string Name, decimal Price) : IRequest<Guid>;

2. Handle the Command

// CreateProductHandler.cs
public class CreateProductHandler : IRequestHandler<CreateProductCommand, Guid>
{
    private readonly IProductRepository _repo;

    public CreateProductHandler(IProductRepository repo)
    {
        _repo = repo;
    }

    public async Task<Guid> Handle(CreateProductCommand request, CancellationToken cancellationToken)
    {
        var product = new Product { Name = request.Name, Price = request.Price };
        await _repo.AddAsync(product);
        return product.Id;
    }
}

3. Define the Query

// GetProductByIdQuery.cs
public record GetProductByIdQuery(Guid Id) : IRequest<ProductDto>;

4. Handle the Query

// GetProductByIdHandler.cs
public class GetProductByIdHandler : IRequestHandler<GetProductByIdQuery, ProductDto>
{
    private readonly IProductRepository _repo;

    public GetProductByIdHandler(IProductRepository repo)
    {
        _repo = repo;
    }

    public async Task<ProductDto> Handle(GetProductByIdQuery request, CancellationToken cancellationToken)
    {
        var product = await _repo.GetByIdAsync(request.Id);
        return new ProductDto(product.Id, product.Name, product.Price);
    }
}

5. Register MediatR in Program.cs

builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(Program).Assembly));

6. Trigger Commands/Queries in Controllers

[HttpPost]
public async Task<IActionResult> Create([FromBody] CreateProductCommand cmd)
{
    var id = await _mediator.Send(cmd);
    return CreatedAtAction(nameof(Get), new { id }, id);
}

Bonus Tip

Use Pipeline Behaviors in MediatR for cross-cutting concerns:


Final Thoughts

CQRS + MediatR helps you write cleaner, decoupled, testable code. It scales well with complex domains and high-load systems.

What’s your favorite CQRS pattern tweak or MediatR trick? Share below.


#dotnet #CSharp #CQRS #MediatR #CleanArchitecture #SoftwareEngineering #DevTips #AsyncProgramming