|
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 friend, I don't really write code by hand anymore. I describe what I want, and the AI builds it. For a while, that scared me. If I am not writing the code, does everything I spent years learning still matter? Does my stack matter at all? If you are a developer right now, I would bet you have felt some version of that too. Here is where I landed. It flipped the whole thing for me. The other day I was debugging a slow app, talking it through with the AI. Would WebAssembly help here? Is it...
Hey friend, Lately I told an AI I’d see it in 7 hours, and went to bed. It was done in 2. Across two different projects. There’s this thing people call the night shift. You give an AI agent a pile of work, start it in a loop, and go to sleep while it keeps working on your real codebase. I tried it a month ago and could not make it work, the model just fell apart. But with the newest models, it works now. And the surprising part wasn’t that it ran all night. It’s that it ran out of work before...
Hey friend, For almost every .NET project I start now, I reach for the same architecture. Not clean architecture. Not the classic controller, service, repository stack that pretty much every tutorial taught us a decade ago, me included... I reach for vertical slice architecture. In my latest video, I break down exactly what it is, why it became my default, and the part almost nobody is talking about. Now that we are all building alongside AI, the rules for choosing an architecture have...