Develop Vertical Slice Feature Folder with CQRS and MediatR in .NET 8 Microservices

Mehmet Ozkaya
4 min readMar 23, 2024

--

We’re going to construct our Catalog.API microservices using Vertical Slice Architecture complemented by the CQRS pattern, all powered by the MediatR library.

In Vertical Slice Architecture, we structure our application around features or functionalities rather than layers. Each vertical slice cuts through all architectural layers, such as UI, business logic, and data access.

I have just published course — .NET 8 Microservices: C# 12, DDD, CQRS, Vertical/Clean Architecture.

🏗️ Setting the Foundation with Feature Folders

First up, let’s structure our microservice:

📁 Feature Folder “Products”

Catalog.API
├── Features
└── Products

The first step is to create a ‘Features’ or ‘Products’ folder.
This is where we’ll organize our code based on domain features. This is where the magic happens. Each feature of our domain, like managing products, gets its own dedicated space.

🛠️ Subfolder for “CreateProduct”

For each use case or feature, like ‘CreateProduct’, we’ll create a corresponding subfolder. This helps in organizing our code based on functionalities and keeps related components together. Inside “Products,” we have a subfolder for each operation, starting with “CreateProduct.” This subfolder will house two crucial classes:

  • CreateProductHandler.cs for the nitty-gritty logic.
  • CreateProductEndpoint.cs for dealing with HTTP requests and responses.

Why I’ve created 2 class inside of feature folder ?

A common approach in Vertical Slice Architecture is to encapsulate all aspects of a feature in one class. However, this can make the code hard to read, maintain and hard to locate actual class.

Instead of that, even we have follow Vertical Slice architecture, inside the Feature folder, I have separated Application Logic and Presentation Endpoints into 2 classes in order to make it more readable and manageble, easy maintanence. This separation makes it easier to locate specific parts of the code and manage them effectively.

🎭 Splitting Roles with CQRS and MediatR

Now, let’s bring CQRS and MediatR into play. We will develop the CreateProduct feature using CQRS and MediatR in our ASP.NET Core application. Let’s start by understanding the lifecycle of a request in a system that uses MediatR in conjunction with CQRS.

CQRS and MediatR Request Lifecycle

Here’s an illustration showing how an API request flows through our system. The request first hits the presentation layer, typically a Carter endpoint in our case, which then sends a command to MediatR.

MediatR’s redirect this command to the corresponding Handler class, and this handler processes this command and returns the result.

📜 Commands and Queries

Our first step is to define the Command and Result classes for the CreateProduct feature. We’ll create these as record types inside our Handler class. CQRS helps us separate our concerns: Commands for actions (writes) and Queries for data retrieval (reads). MediatR treats these as messages to be handled.

🦸 Handlers to the Rescue

For every command or query, there’s a handler ready to tackle it. These handlers are the workers, executing the specified tasks.

🚀 Implementing “CreateProduct” Feature

Let’s dive into the “CreateProduct” feature with some code:

🎖️ Command and Result Classes

We define what data we need for creating a product and what we’ll return upon success.

public record CreateProductCommand(string Name, List<string> Category, string Description, string ImageFile, decimal Price) : IRequest<CreateProductResult>;
public record CreateProductResult(Guid Id);

In this code, CreateProductCommand represents the data we need to create a product, and CreateProductResult is what we’ll return, which in this case, is just the product’s ID.

🛠️ The Handler Class

Our CreateProductCommandHandler is where the command gets processed:

public record CreateProductCommand(string Name, List<string> Category, string Description, string ImageFile, decimal Price)
: IRequest<CreateProductResult>;

public record CreateProductResult(Guid Id);


internal class CreateProductCommandHandler : IRequestHandler<CreateProductCommand, CreateProductResult>
{
public Task<CreateProductResult> Handle(CreateProductCommand request, CancellationToken cancellationToken)
{
// Business logic to create a product
// For now, let’s throw a NotImplementedException
throw new NotImplementedException();
}
}

This class listens for CreateProductCommand and returns CreateProductResult with a new product ID. Note that we're simulating product creation here for simplicity. With this setup, we can use MediatR’s _mediator.Send method to trigger the command and handle it in this class. This will be works very well.

🌉 From HTTP Request to Handler

How does an HTTP request trigger this handler? That’s where CreateProductEndpoint.cs and MediatR's magic come in:

app.MapPost("/products", async (CreateProductCommand command, IMediator mediator) =>
{
var result = await mediator.Send(command);
return Results.Created($"/products/{result.Id}", result);
});

This endpoint listens for POST requests to “/products”, creates a CreateProductCommand from the request body, and sends it off to MediatR, which then finds and invokes the corresponding handler.

🚀 Launching the Feature

With this setup, our “CreateProduct” feature is not just a piece of code; it’s a well-organized, easily maintainable, and scalable unit that follows the principles of Vertical Slice Architecture and CQRS, all orchestrated beautifully by MediatR.

Stay tuned as we continue to explore and implement more features using this elegant architecture, diving deeper into the world of ASP.NET Core microservices.

I have just published course — .NET 8 Microservices: C# 12, DDD, CQRS, Vertical/Clean Architecture.

This is step-by-step development of reference microservices architecture that include microservices on .NET platforms which used ASP.NET Web API, Docker, RabbitMQ, MassTransit, Grpc, Yarp API Gateway, PostgreSQL, Redis, SQLite, SqlServer, Marten, Entity Framework Core, CQRS, MediatR, DDD, Vertical and Clean Architecture 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