CQRS & MediatR | ASP.NET 6 REST API Following CLEAN ARCHITECTURE & DDD Tutorial | Part 6

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi everyone and welcome to the long-awaited video about mediator and cqrs so if any of the words that you see on the screen seem interesting to you then make sure to watch until the end i think you're going to like it okay so first let's start with cqs so cqs which was first invented by this guy over here originally only talked about methods so basically you have two kinds of methods ones that change data manipulate the database or change the state of the object if so then they're called a command and they're not allowed to return a value so basically void and the other kind of methods do return a value they ask a question but if so they must be pure and they're not allowed to change the state of any object okay so let's look at an example so over here we have a definition of some stack and let's look at these methods so push it's easy to see that this over here is a command so it receives a value it changes the state but it's void it doesn't return a value peak also it's pretty easy to see that this is a query right it asks the question what's the top element and it returns it okay now looking at pop what is it is it a command because it changes the state right it pops the first value or is it a query right it returns the value that was just popped so the original definition of cqs is very strict and this method doesn't have a place in the cqs world you know me and you aren't the only ones that think this definition is a bit strict so both martin fowler and greg young both talk about the fact that this is a bit strict and not always practical the pop example is actually taken from martin fowler and that's why greg young came up with cqrs so greg young also likes the separation between the commands and the queries but what he sees as more important is having clear boundaries between what manipulates data and what doesn't manipulate data and that's why it puts an emphasis on splitting the logic to two different classes or two different objects one which manipulates data and one that doesn't okay so going back to our stack example so basically we would have two separate interfaces one with our rights and one with our reits okay the way you separate it is basically you always ask am i changing data if yes then it's a command otherwise if the method only reads data then it sits over here in the read object a very common misconception about cqs and cqrs is that this design pattern talks about the entire application and you need to have two separate applications one for reading from the database and one for writing to the database or another misconception is that you need to have at least two databases so one read database and one write database so if you go back to the original documentation of the people who coined the terms then you'll see it has nothing to do with separate applications of course these design patterns that now we're looking at them in the micro level of a class can be taken to a macro level and then we can look at it also as an entire application how we can split it but you aren't required to do any of that so today i want to take a non-trivial example like the authentication service which has the register and the login methods and ask ourselves how we would split it following the cqrs design pattern okay so let's get started okay so i'm in our authentication service where we have the register and login methods so let's start with the obvious so register is obviously a command we're adding the user to the database so let's go ahead and change this to i authentication command service let's update the file name as well then in the authentication service then the implementation as well let's change it to command okay now we want to leave the register method as part of the service but what about the login method so basically the login method has two parts it checks if the user is in the database and if he does then it returns a new token which the user can now use to authenticate against the service i like asking myself am i actually changing any data am i mutating any state and since over here we aren't so the login method is actually a query we can call this instead of login maybe get token or get authentication details or a name that makes it more clear that we aren't changing anything but it makes sense to put in a query service that way whoever needs to use the login method has a clear boundary that is only retrieving data and is not also updating data okay so let's go ahead and create also the query service okay to make things a bit clearer first of all let's change the name of this file to authentication command servers as well let's create here a new folder it's called commands let's move both of these files inside next let's create here a new folder is called queries and for now let's copy paste these two files over here then from the authentication command service let's remove the login method both from the interface and the implementation and in the query side let's search for everywhere wrote command and let's switch it with query let's update the file name and let's do the same thing also over here great let's remove the register method both from here and from here let's update the file name the query and that should be it right so we have the command folder with a command service and we have the queries with the query service great let's create a new folder with anything that's common for both of them and move authentication result inside and last but not least let's update all the namespaces to match the folder structure so both over here here and here as well next let's go to the dependency injection and make sure both of our services are registered so this is the same only that command should be query and back in the authentication controller let's change the name of this to authentication command service and we'll also have here the query service so the same thing just with query let's include the namespace and add it to the constructor let's just wrap the line so it's visible okay now the register uses the command service and in the login we want to use the query service great let's build a project and make sure that this works so dotnet build great let's run the service okay register command worked successfully and from the previous video if we try to register the same user again then we get our error messages let's make the login request let's just fix the password and great our login query works as we expect okay so let's look at what we have so all we had in the beginning was a single authentication service which we split into both the command service and the query service and i want us to think together how the solution will scale if you've ever worked on a large system then you know that sometimes these service files can become 7000 lines of code of every developer just saying yeah i think it goes over here and adding his specific logic so instead of having a single file now we'll basically have two files where each one of them is also a god file if you experience something like this then you might like the next approach that we're going to take which is splitting the application layer not by these various services but rather by the features or use cases that we have so let's see what that might look like okay so very similar to what we had before so we have our commands and we have our queries instead of having over here the various services what we have is we'll have the various features that have to do with authentication and what this means is that as our application grows it grows horizontally with more and more isolated features instead of growing vertically and that means a single file with all of our logic and all of our dependencies in one place okay now i want us to zoom out actually and look at our system as a whole so in clean architecture as we discussed the presentation layer is the door to the outside world so in our case in the request flow then it's the controllers it's the endpoints that we have translating the data that we get in the request to the language that the core logic of our application knows how to speak and the response flow so it's taking whatever answer the application layer returns to the presentation layer and converting it to the appropriate response now focusing on the application layer we want the application layer to have the use cases in our system one of the reasons that i really like this approach is that it plays really nicely with clean architecture so you look at the application layer and it's very obvious what the use cases of our system is so two years from today when this application has many features then you can look at application layer and it's very easy to see which use cases we have and what resource or what category they belong to so this brings us to the next part of our video which is using the package mediator and the way this will work is we have our authentication controller sending either a command or a query to the mediator and the mediator will invoke the appropriate command or query handler based on the command or query that we pass it okay so if you aren't familiar with this then this will make more sense as we implement it so back in our application let's add the mediator package to the application layer so that might add to the application layer the package mediator great now that we have that we can go to our controller and add the mediator interface so private read-only i mediator mediator great let's close the terminal okay now this mediator will replace these services that we have over here so we won't need them anymore okay and the way this works is that over here in our presentation layer all we need to do is create here some command yes so this doesn't exist yet but let's imagine we have something similar to this and send this to the mediator right so something like this then the mediator will invoke the appropriate logic and will return the appropriate response so let's look at how something like this is implemented okay so over here in the application layer let's create a new folder it's called authentication inside command and over here a folder register here let's define our register command so register command so public record register command and what we want to put over here is yes actually that this seems right let's just wrap it because i actually want to open side by side the register command and the register request you can see that in this case they look exactly the same this won't always be true and all the command is it encapsulates the data that we need to execute the command so in this case it's exactly what we got in the request okay so now back in our controller we can include this and you can see we get here an error that's because how is the mediator supposed to know that it returns this value so back in our command we need to define the return value so i request error or authentication result add this so complete definition is this is the data that we need and this is what this command returns okay so back in the authentication controller this is actually synchronous so let's add here await and let's fix the signature great now let's add the handler let me just close it so it's more organized so authentication register command let's create here the register command handler which yes let me just get rid of all this actually let's include what we need to include arrow or yes and let's implement the interface yes let's just put this on a new line so it's more readable okay so all we have over here is the command handler which as you can see implements this interface from the mediator package and it receives the command that it handles and the response that we're returning okay now to handle this let's copy the logic from the register method so let's copy everything over here and paste it over here next let's copy the two fields that we need and paste it here as well and let's include everything we need to include great next let's create a constructor yes thank you okay next the details are coming from the request right so let's change the name to command and over here let's have command that first name last name email and password same goes over here for the email okay and last thing let's make this synchronous and that's it okay so again all we have here is the specific dependencies that we need for this command handler and down here we have only the logic that we need to register the user let's do the same thing for the login logic so queries in here login and in here let's have the login query and the login query handler let's copy this entire thing and paste it over here this instead of register will be login it will receive only the email and the password and it will return the exact same thing yes let's just fix the namespace next let's copy the entire handler and paste it over here let's actually just change every place where it says register to log in okay this is the login query and this doesn't work because yes this should be query great let's get rid of this entire thing i'll let it generate the correct method for us then we can go to the query service and copy the entire login logic over here add async change request to query and get values from this object and that seems about right let's go ahead and get rid of this entire services folder and back in the dependency injection let's get rid of this and these two and over here we want some magic which knows how to wire everything together so services dot add mediator and pass it our assembly which is dependency injection okay now this isn't recognized because it comes from a different package so let's add it dotnet add to the application layer the package mediator dot 10 let's see if it's a complete entire thing no microsoft dot dependency injection yes perfect now let's add it you know if you're curious how this actually works so over here you can see that there's some logic that scans the assembly and connects everything together so this is specifically the interface that we implemented now mediator has many really cool features and we'll actually cover i think almost all of them in the series so if you're curious about notifications behaviors and so on then make sure to subscribe because by the end of the series then you should have a very good understanding of all the features that we're going to cover great so now that we have everything wired together let's go back to the authentication controller and get rid of these using statements yes so one thing i did which i shouldn't have done but we're going to delete the authentication result object so let's recreate it so common authentication result yes only that this should be capital and let's add what we need to add right back over here let's add the name space and fix this here as well so this comes from our new definition same goes over here and over here and last but not least over here as well great now let's replace this with our mediator implementation so mediator dot send here we want to send the login query so query yes the login query let's pass it over here and await the response let's fix this here okay now one pro tip about the mediator interface so if we look at the interface you can see we actually have here two interfaces the isender has the send method which we talked about and the ipublisher something else which we'll talk about in the future okay and because we really like the interface segregation principle then we prefer not using the imediator interface but rather simply using only the ice sender so let's just replace everywhere it says imediator with i sender great now that we have that let's just make sure that it builds as we expect perfect let's run it okay now actually wants to put a breakpoint both over here and over here and also in the corresponding handlers so in the register command handler and in the login command handler over here great let's attach the debugger so f5 and let's choose our application okay now that we have it attached let's make the register request okay we hit the breakpoint we mapped our request to the corresponding command okay let's continue and we can see that the correct handler was invoked like we expect let's make sure that the login works as well so we make the request we arrive at the controller we map it to the corresponding query and we see that the correct handler was invoked here as well right let's continue and here we have our response okay now one last thing i want to talk about so i want us to look at the login query you can see over here we're returning the authentication result right and in the authentication result we're returning here the user object now this user object is a domain entity and we'll see in the future it's actually going to be one of our aggregates it's important to note that one of the main motivations for cqrs in domain driven design is to have the response as slim as possible so to have a slim dto which contains only the data that i actually want to return from the query and not like we're doing over here where basically we're loading in the future an entire aggregate which probably has a lot of data and it encapsulates a lot of logic that we don't care about in the query flow okay so that's just one thing to note but i'm going to leave it as is for now so that's it for this video i really hope you understand the rationale behind this design choice and why even though it adds a layer of complexity this might be something to consider in your applications in the future videos we'll talk about some other pretty cool features of the library mediator and we'll also start talking a bit more about domain driven design principles which we'll see how everything plays really really nicely together so if that sounds intriguing make sure to subscribe and i'll see you in the next one
Info
Channel: Amichai Mantinband
Views: 54,506
Rating: undefined out of 5
Keywords:
Id: MwMVvLBSJa8
Channel Id: undefined
Length: 19min 15sec (1155 seconds)
Published: Sun Jul 10 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.