Introduction
In this post I will cover the process of building a modern, modular and scalable ASP.NET Core application using CQRS (Command Query Responsibility Segregation) with MediatR and Feature Slicing Architecture by using ASP.NET Core Minimal API.
You can clone the repository with project here: Github – FeatureSlices
Feature Slicing implementation

Layers/Slices Explanation
Model:
- Books Model: Represents the data structure for books
- Movies Model: Represents the data structure for movies
public class Movie
{
public int Id { get; set; }
public string? Title { get; set; }
public string? Director { get; set; }
}
Repository:
- Books Repository: Handles data access and persistence operations for books
- Movies Repository: Handles data access and persistence operations for movies
public async Task AddMovieAsync(string title, string director)
{
var movie = new Movie { Title = title, Director = director };
_context.Movies.Add(movie);
await _context.SaveChangesAsync();
}
MediatR handlers:
- Book Commands/Queries Handler: Handles MediatR requests for books (e.g., commands and queries)
- Movie Commands/Queries Handler: Handles MediatR requests for movies (e.g., commands and queries)
public async Task<Unit> Handle(CreateMovieCommand request, CancellationToken cancellationToken)
{
await _repository.AddMovieAsync(request.Title, request.Director);
return Unit.Value;
}
Controller:
- Books Controller: Manages HTTP requests and responses for books
- Movies Controller: Manages HTTP requests and responses for movies
public static void MapCreateMovieEndpoints(this WebApplication app)
{
app.MapPost("api/movies", async (CreateMovieCommand command, IMediator mediator) =>
{
await mediator.Send(command);
return Results.Ok();
});
}
CQRS (Command Query Responsibility Segregation) implementation

Why This Approach is Good
- Separation of Concerns:
- Models, Repositories, Services, Controllers, and MediatR are organized into distinct layers. Each layer has a clear responsibility which makes the codebase easier to manage and understand. This separation helps in isolating changes and reduces the risk of bugs in unrelated parts of the system
- CQRS with MediatR:
- CQRS (Command Query Responsibility Segregation) separates read and write operations into distinct models, improving scalability and performance. MediatR facilitates this by decoupling request handling from the core business logic and implementing a mediator pattern making it easier to manage and extend requests and handlers
- Encapsulation of Business Logic:
- Services handle business logic while Repositories handle data access. This separation ensures that business rules are not tangled with data access code, making it easier to adapt business logic without affecting the data layer
- Improved Testing:
- Unit Testing is straightforward because components are isolated. You can test Services independently of the Controllers and Repositories and MediatR handlers can be tested in isolation. This isolation reduces the complexity of testing and improves test reliability
- Flexibility and Scalability:
- The architecture is flexible and allows easy addition of new features or modifications to existing ones. The clear separation between commands, queries, and handlers means that changes in one part of the system have minimal impact on others making the system more scalable
- Enhanced Maintainability:
- With clear separation, maintenance tasks such as bug fixes, feature updates, and code refactoring become more manageable. Each layer can be updated independently and the impact of changes is localized.
Why This Approach Might Be Bad
- Increased Complexity:
- The architecture introduces multiple layers and components which can make the system more complex. For smaller applications or simple use cases this complexity might be unnecessary and could lead to over-engineering
- More Boilerplate Code:
- Implementing CQRS and using MediatR adds additional boilerplate code. For each command or query you need corresponding handler classes and interfaces. This can result in more code to write and maintain compared to simpler patterns
- Overhead in Learning Curve:
- For teams unfamiliar with CQRS or MediatR there is a learning curve. Understanding how these patterns and libraries work can require additional training and effort which will potentially slow down development
- Potential for Over-Engineering:
- The approach might be considered over-engineered for simple scenarios where a more straightforward CRUD approach would suffice. For applications that do not require the scalability or separation benefits of CQRS this architecture could introduce unnecessary complexity
- Performance Considerations:
- The additional indirection introduced by MediatR and the separation of commands and queries might introduce some performance overhead. For applications with high-performance requirements, this overhead might need to be carefully managed