CQRS and MediatR Request Lifecycle: Developing Feature Handler Classes
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.
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
- 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.
- 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.
- Handler Execution: MediatR identifies the appropriate handler for the command. The handler contains the business logic needed to process the command.
- 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:
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.
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:
- Command or Query Classes: Ensure your command or query classes implement the
IRequest<Result>
interface. - 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.
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.