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<int> 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 IRequestHandler<CreatePlayerCommand, int> interface, 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<Player?>, indicating that it will return a Player object or null.

Next, we need a handler to process this query:

It implements IRequestHandler<GetPlayerByIdQuery, Player?> 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, Ever tried to choose the perfect weapon for each creature in a dark, spooky forest? Silver bullets for werewolves, garlic for vampires, chainsaws for zombies... but what if you had to pick your weapon before entering the forest? Just like that forest adventure, dependency injection in C# can leave you stuck with the wrong tool when you need it most - unless you know about the Factory Pattern! Today, I’m breaking down how the Factory Pattern solves real-world dependency injection...

video preview

Hey Reader, In today’s tutorial, I’ll walk you through how to use Dapper, a high-performance micro ORM (object-relational mapper) with a .NET 8 Web API and SQL Server. We’ll cover everything you need to know to build a simple CRUD (Create, Read, Update, Delete) application. Watch on YouTube 📺 Check out the full tutorial on YouTube to see everything in action 👇 What We’ll Be Doing We’re going to work with a simple video game database to start. You’ll learn how to: Read all games or a specific...

video preview

Hey Reader, Today, I show you how combining Blazor and JavaScript can give you the best of both worlds. Let’s dive into how you can easily mix the power of these two technologies in your projects! Watch on YouTube 📺 Check out the full tutorial on YouTube to see everything in action 👇 A Simple Example: Console Logging in Blazor Imagine you’re working on a Blazor Server application, and you want to log the current count (of the Counter page) to the console. Your first thought might be to use...