CQRS & MediatR in a .NET 8 Web API 🚀


Hey Reader,

Today, we're going to dive into CQRS (Command and Query Responsibility Segregation) and the Mediator pattern using a .NET Web API. We'll keep things simple by not using clean architecture for this example. If you're interested in CQRS and the Mediator pattern, this tutorial is for you. You can either watch the video tutorial on YouTube or keep scrolling if you prefer reading. Let's get started!

YouTube Video Tutorial

Check out the video below for the full tutorial.

video preview

What is CQRS?

CQRS stands for Command and Query Responsibility Segregation. It involves separating the operations that modify data (commands) from those that read data (queries). Here’s a brief overview:

  1. Command: An operation that changes data (e.g., create, update, delete).
  2. Query: An operation that reads data.

What is the Mediator Pattern?

The Mediator pattern is used to reduce the dependencies between objects. Instead of objects communicating directly, they communicate through a mediator. In a .NET application, we can use the Mediator library to implement this pattern.

Setting Up the Project

First things first, let's create a new Web API project in Visual Studio with .NET 8 and install the necessary NuGet packages, which would be MediatR and Microsoft.EntityFrameworkCore.InMemory if you want to use an in-memory database for this example project.

Implementing CQRS and the Mediator Pattern

Set Up the Data Model and In-Memory Database

We're going to build a simple video game API where we can create players and query single players by their Id. So we need a Player class first.

Configure the in-memory database and MediatR

Next comes the database context. You can configure it in the following way:

Don't forget to register the context in the Program.cs. While we're already at it, we can also add and configure the MediatR package there as well.

Now it's time to create commands and queries.

Implement Commands and Handlers

A command represents an action that changes the state of the application. It is used to create, update, or delete data. Create a Features folder to organize our commands and queries.

For this command, we're using the IRequest interface. It is part of MediatR and indicates that this command will return an integer upon completion. In this case, it will return the Id of the newly created player. Name and Level are the properties needed to create a player. They are passed to the command when it is instantiated.

Corresponding to that command comes the command handler:

This handler implements the IRequestHandlerinterface, indicating it handles CreatePlayerCommand requests and returns an int (the Id of the newly created player). We're using the VideoGameAppDbContext instance to interact with the database and in the Handle method, a new Player object is created with the name and level provided in the CreatePlayerCommand request, added to the context, and saved to the database. Finally, it returns the Id of the created player.

Next comes the query.

Implement Queries and Handlers

First, we add the GetPlayerByIdQuery.

It takes an int Id parameter and implements IRequest, indicating that it will return a Player object or null.

Next, we need a handler to process this query:

It implements IRequestHandler and so indicates that it handles GetPlayerByIdQuery requests and, again, returns a Player object or null. In the Handle method, we use the FindAsync method of the context to retrieve the player by their Id and return the player.

Finally, we need to use the command and the query in our controller:

Implement the Controller

The controller's constructor injects an ISender parameter from MediatR, which is used in the CreatePlayer POST method to send a CreatePlayerCommand and return the new player's Id, and in the GetPlayerById GET method to send a GetPlayerByIdQuery, returning the player or a 404 status if not found.

Summary

Commands are used to modify the application's state. They encapsulate the data needed for the operation and are handled by command handlers which contain the logic to perform the action.

Queries are used to retrieve data without modifying the state. They encapsulate the criteria for the data retrieval and are handled by query handlers which contain the logic to fetch the data.

By using the CQRS and Mediator patterns, we achieve a clear separation of concerns, making our codebase easier to maintain and extend. Each handler focuses on a specific responsibility, and the controller remains clean, only coordinating the sending of commands and queries to their respective handlers.


If you have any questions or need more details, just hit reply to this email. I'm here to provide more tutorials and answer any queries you might have.

Thank you so much for your support. You help keep this channel growing. Stay safe!

Take care & happy coding,

Patrick

P.S. If you're ready to level up your .NET and Blazor skills, check out the .NET Web Academy. You'll find in-depth courses, all my YouTube source codes, and a wonderful community of like-minded developers. 🚀


Patrick God

Become a .NET & Blazor expert with weekly tutorials featuring best practices and the latest improvements, right in your inbox.

Read more from Patrick God
video preview

Hey Reader, You may have seen the recent blog post from Jimmy Bogard (creator of MediatR and AutoMapper) announcing that these libraries are going commercial. And to be clear, I fully support that. Time is money, and supporting your family always comes first. BUT… In most of my projects, I don’t actually use everything MediatR offers. I usually just need a small part of it, the classic "send a request, get a response" pattern. So I thought: why not build a simple version myself? 👉 That’s...

video preview

Hey Reader, In my last video, I showed you how to build a CRUD app using Vertical Slice Architecture and controllers in .NET 9. But… What if you could do the same thing - cleaner, simpler, and without all that controller clutter? 👉 That’s exactly what I show you in my brand-new video: In this one, you’ll learn how to: Use Minimal APIs instead of controllers Keep your Program.cs neat and tidy Stay fully organized with the Vertical Slice Architecture Work with Carter, a super helpful library...

video preview

Hey Reader, If you’ve ever built a simple CRUD app with a layered architecture… you might be making things harder than they need to be. In my newest YouTube tutorial, I show you how to build a scalable CRUD app using something super clean and refreshing: 👉 Vertical Slice Architecture in .NET 9 We’ll build a full Video Game API, step-by-step, with: .NET 9 Web API + Entity Framework Core Code-first migrations Controllers (yep, still using those!) The Mediator pattern for cleaner code And, of...