CQRS and MediatR Request Lifecycle: Developing Feature Handler Classes

Mehmet Ozkaya
4 min readSep 3, 2024

--

We’re going to explore a key architectural pattern in modern software development: CQRS (Command Query Responsibility Segregation) and how it works in conjunction with MediatR.

CQRS and MediatR Request Lifecycle

Get Udemy Course with limited discounted coupon — .NET Backend Bootcamp: Modulith, VSA, DDD, CQRS and Outbox

We’ll focus on developing a feature handler class using these concepts in our ASP.NET Core application. Let’s dive into the lifecycle of a request when using MediatR and CQRS! 🚀

Understanding the CQRS and MediatR Request Lifecycle

Before we jump into coding, it’s crucial to understand how a request flows through a system that employs MediatR and CQRS.

How It Works

  1. API Request Initiation: When a client makes an API request, it first hits the presentation layer. In our setup, this is typically a Carter endpoint.
  2. Command Dispatch: The endpoint then creates a command object representing the client’s request. This command is sent to MediatR, an in-process messaging library that acts as a mediator.
  3. Handler Execution: MediatR identifies the appropriate handler for the command. The handler contains the business logic needed to process the command.
  4. Processing and Response: The handler executes the logic, interacts with the database if necessary, and returns a result. This result is then sent back to the client as the response.

Here’s a visual representation of the lifecycle:

CQRS and MediatR Request Lifecycle

Key Components in the Lifecycle

  • Command: A class that encapsulates all the data required for a specific operation (e.g., creating a product).
  • Result: A class that defines what will be returned after the command is executed (e.g., the ID of the newly created product).
  • Handler: A class that implements the logic for processing the command and producing the result.

Developing the CreateProduct Command and Handler

Now, let’s go step-by-step in developing the CreateProductCommand and its corresponding handler in our Catalog module.

Catalog Module Internal Architecture

Step 1: Create the Command Class

Navigate to the Catalog module and create the CreateProductCommand class.

public record CreateProductCommand(ProductDto Product) : ICommand<CreateProductResult>;
public record CreateProductResult(Guid Id);

Step 2: Implement the Handler

Next, create a file named CreateProductHandler.cs in the same directory. Implement the handler as shown below:

internal class CreateProductHandler : ICommandHandler<CreateProductCommand, CreateProductResult>
{
private readonly CatalogDbContext _context;
public CreateProductHandler(CatalogDbContext context)
{
_context = context;
}
public async Task<CreateProductResult> Handle(CreateProductCommand command, CancellationToken cancellationToken)
{
// Create Product entity from command object
var product = new Product
{
Id = Guid.NewGuid(),
Name = command.Product.Name,
Category = command.Product.Category,
Description = command.Product.Description,
ImageFile = command.Product.ImageFile,
Price = command.Product.Price
};
// Save to database
_context.Products.Add(product);
await _context.SaveChangesAsync(cancellationToken);
// Return result
return new CreateProductResult(product.Id);
}
}
  • Dependency Injection: We inject CatalogDbContext into the handler to interact with the database.
  • Handler Logic: The Handle method converts the command into a product entity, saves it to the database, and returns the result.

Triggering the Handler with MediatR

How do we trigger this handler? That’s where MediatR comes into play. MediatR allows us to trigger command and query handlers using a mediator pattern. To use MediatR effectively:

  1. Command or Query Classes: Ensure your command or query classes implement the IRequest<Result> interface.
  2. Handler Class: Implement the IRequestHandler<Command, Result> interface in your handler class.

Here’s how you would typically invoke the handler in your application code:

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

Conclusion

By following these steps, we’ve successfully set up a CQRS pattern with MediatR in our ASP.NET Core application. This architecture provides a clean separation of concerns, making your application more maintainable and scalable.

Get Udemy Course with limited discounted coupon — .NET Backend Bootcamp: Modulith, VSA, DDD, CQRS and Outbox

EShop Modular Monoliths Architecture w/ Catalog, Basket, Identity and Ordering modules

This is step-by-step development of reference Modular Monoltihs Architecture on .NET used ASP.NET Web API, Docker, PostgreSQL, Redis, RabbitMQ, Keycloak, Seq, MassTransit, Entity Framework Core, CQRS, MediatR, DDD, Vertical Slice Architecture and Outbox Pattern implementation with using latest features of .NET 8 and C# 12.

--

--

Mehmet Ozkaya
Mehmet Ozkaya

Written by Mehmet Ozkaya

Software Architect | Udemy Instructor | AWS Community Builder | Cloud-Native and Serverless Event-driven Microservices https://github.com/mehmetozkaya

No responses yet