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:

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:

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:

With CQRS:

This pattern is widely used in scalable enterprise applications.

Basic Structure of CQRS

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

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:

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:

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:

Queries:

This separation helps scale read and write operations independently.

Common Mistakes to Avoid

Advantages of CQRS Pattern

Disadvantages of CQRS Pattern

When Should You Use CQRS?

Use CQRS when:

Avoid when:

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.