|
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 TutorialCheck out the video below for the full tutorial. 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:
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 ProjectFirst 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 PatternSet Up the Data Model and In-Memory DatabaseWe'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 MediatRNext 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 HandlersA 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 HandlersFirst, 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 ControllerThe 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. SummaryCommands 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. 🚀 |
Become a .NET & Blazor expert with weekly tutorials featuring best practices and the latest improvements, right in your inbox.
Hey Reader, .NET 10 just dropped. 🥳 And if you’ve been waiting for a reason to build something new, this is it. In my latest tutorial, I’ll walk you through how to build a complete Web API from scratch in .NET 10, including: Setting up Controllers Using Entity Framework Core 10 Connecting SQL Server Working with DTOs the right way It’s perfect if you’re starting fresh or want to upgrade an older .NET 8 or .NET 9 project to the newest version. Watch the full tutorial here 👇 Take a look, and...
Hey Reader, .NET 10 is almost here, and it brings a big upgrade for validation in Minimal APIs. In this week’s tutorial, I’ll walk you through how validation works now in .NET 10 (using Entity Framework, Scalar, and Minimal APIs). Before, writing validation logic in each endpoint was a pain. You had to manually check for null values, invalid quantities, or missing fields. But in .NET 10, that’s all built-in and super clean. Watch the full tutorial now: 👇 Happy coding! Take care, Patrick
Hey Reader, If you’ve been coding for a while, you’ve probably asked yourself this too: “Am I getting better… or just older?” I’ve been writing software for fifteen years now, and that question still sneaks up on me. But looking back, I’ve realized something: real growth in tech isn’t about frameworks or chasing trends. It’s about staying curious, patient, and keeping your spark alive when everything feels heavy. So I made a new video about it, my 15 biggest lessons from 15 years of coding....