ASP.NET Core  

How to Implement CQRS Pattern in ASP.NET Core Web API

Introduction

When building modern web applications using ASP.NET Core Web API, one common challenge developers face is managing complex business logic while keeping the code clean, scalable, and easy to maintain.

As applications grow, mixing read and write operations in the same logic can make the system harder to manage. This is where the CQRS (Command Query Responsibility Segregation) pattern becomes very useful.

In simple words, CQRS separates the operations that change data (commands) from the operations that read data (queries).

In this article, you will learn how to implement the CQRS pattern in ASP.NET Core Web API step by step using simple language, real-world examples, and practical understanding.

What is CQRS Pattern?

CQRS stands for Command Query Responsibility Segregation.

It means:

  • Command → Used to create, update, or delete data

  • Query → Used to read or fetch data

Instead of using a single model for both reading and writing, CQRS separates them into different models and logic.

Real-life example:

Think of a banking system:

  • Depositing money → Command (changes data)

  • Checking balance → Query (reads data)

Both operations are different and should be handled separately.

Why Use CQRS in ASP.NET Core Web API?

Using CQRS improves the structure of your application.

Without CQRS:

  • Business logic becomes mixed

  • Controllers become heavy

  • Difficult to scale

With CQRS:

  • Clear separation of concerns

  • Better maintainability

  • Easier testing

  • Improved performance optimization

This pattern is widely used in scalable enterprise applications.

Basic Structure of CQRS

In a CQRS-based ASP.NET Core application, we usually have:

  • Commands (Write operations)

  • Queries (Read operations)

  • Handlers (Process commands and queries)

  • Models (Separate for read and write)

This separation helps organize the code better.

Step 1: Create ASP.NET Core Web API Project

Start by creating a new Web API project.

Using CLI:

  • dotnet new webapi -n CQRSExample

  • cd CQRSExample

Or use Visual Studio and select ASP.NET Core Web API with .NET 8.

Step 2: Install MediatR Library

To implement CQRS cleanly, we use MediatR, a popular library for handling requests.

Install package:

  • dotnet add package MediatR

  • dotnet add package MediatR.Extensions.Microsoft.DependencyInjection

MediatR helps us send commands and queries to their respective handlers.

Step 3: Configure MediatR in Program.cs

Add MediatR service:

builder.Services.AddMediatR(typeof(Program));

This registers all handlers automatically.

Step 4: Create Command (Write Operation)

Example: Create a product

public class CreateProductCommand : IRequest
{
public string Name { get; set; }
public decimal Price { get; set; }
}

This command represents data required to create a product.

Step 5: Create Command Handler

public class CreateProductHandler : IRequestHandler<CreateProductCommand, string>
{
public Task Handle(CreateProductCommand request, CancellationToken cancellationToken)
{
// Simulate saving data
return Task.FromResult("Product Created: " + request.Name);
}
}

Handler contains the actual business logic.

Step 6: Create Query (Read Operation)

Example: Get product details

public class GetProductQuery : IRequest
{
public int Id { get; set; }
}

Step 7: Create Query Handler

public class GetProductHandler : IRequestHandler<GetProductQuery, string>
{
public Task Handle(GetProductQuery request, CancellationToken cancellationToken)
{
// Simulate fetching data
return Task.FromResult("Product Details for ID: " + request.Id);
}
}

Step 8: Use CQRS in Controller

[ApiController]
[Route("api/[controller]")]
public class ProductController : ControllerBase
{

private readonly IMediator _mediator;

public ProductController(IMediator mediator)
{
    _mediator = mediator;
}

[HttpPost]
public async Task<IActionResult> Create(CreateProductCommand command)
{
    var result = await _mediator.Send(command);
    return Ok(result);
}

[HttpGet]
public async Task<IActionResult> Get(int id)
{
    var result = await _mediator.Send(new GetProductQuery { Id = id });
    return Ok(result);
}

}

Now controller becomes clean and simple.

Step 9: Real-World Example

In an e-commerce app:

Commands:

  • Add product

  • Update product

  • Delete product

Queries:

  • Get product list

  • Get product details

This separation helps scale read and write operations independently.

Common Mistakes to Avoid

  • Mixing command and query logic

  • Not using handlers properly

  • Overcomplicating small applications

Advantages of CQRS Pattern

  • Clean architecture

  • Easy to scale

  • Better separation of concerns

  • Improved maintainability

Disadvantages of CQRS Pattern

  • Adds complexity

  • More files and structure

  • Not needed for small projects

When Should You Use CQRS?

Use CQRS when:

  • Application is large

  • Business logic is complex

  • High scalability is needed

Avoid when:

  • Project is small

  • Simple CRUD operations only

Summary

CQRS pattern in ASP.NET Core Web API helps separate read and write operations, making applications more structured, scalable, and maintainable. By using tools like MediatR and following a step-by-step implementation, developers can build clean and efficient APIs. While it may add some complexity, the long-term benefits make it a powerful pattern for modern application development.