asp.net core - MediatR (CQRS) Tutorial & Tips

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
welcome everyone today we're gonna be talking about the mediator and CQRS pattern and now there is a couple of videos out there that explain the topic pretty well what I'm aiming to do is to show you a bare-bones example and hopefully cover some ground that I think others haven't touched upon specifically on how to work with mediator there are some good presentation talks on in DC I can't remember the guys name but the project that he works on north traders I'm gonna link I'm gonna leave a link for that in the description that's a very good example and there was also an explanation by Nick Chapas that's very good but again he just dives in until like an application that has a bunch of stuff so yeah today I'm gonna just bring a bare-bones example and we're gonna build stuff up step by step and I'm gonna just show you what is available and what you can use okay sorry guys just a quick intermission as I was editing this video I forgot to mention a few things precisely why would you want to use mediator or the CQRS pattern and first and foremost is if you're not working on production code and you just want to learn it that is a perfect reason to try it go ahead try to learn securest see what it's about and see what benefits you can reap from it right so the second reason you might want to use the secured as pattern and mediator is if your services are calling each other right so because you have this box in the middle where when you want to read or write all of them go through the mediator in the middle so our request comes in and then it goes out so if you want to trigger any request from anywhere it just has to hit that box in the middle the third thing is if you have a large project and you would like cleanliness in this video you will see how the use of mediator and it's pipelines will help you shrink your controller sizes and basically move your business logic into their respective files and it will be very easy to traverse the folder structure and find the logic that you're looking for that you think may be at fault fourth point is separation of writes and reads right so if you for example work in a big team and you have a UI team which might want to implement some other UI feature that will require more data from the database they can just work on that and the the reads are separated from the write so they can work freely without having an impact on the let's say the model team and the team that works on the backend and how that logic may distribute notification or rights to some other channel to trigger further services etc right so if you want to have that split go ahead and implement it and also the final point that I would bring is a code portability since you're gonna push all your services into a separate project and from that point on all of those services are again triggered from the box that is the mediator and anywhere where you want to reuse that code you just go ahead and bring the box along with you import the project and there you go all your services are again accessible through this box now if you are working on a production application and the domain model is simple and you don't need to be doing a lot of stuff when you're writing to the database so if the notifications are not going out if you're literally just maybe updating a configuration record or something like that you don't need CQRS okay if a simple crud API will solve the job by implemented it does bring complexity in terms of that you will have to create more files etc however one is you build a simple crud API with proper abstractions will result in a lot less code and a lot less complexity okay and that's pretty much it so let's go ahead and get back to the video so simple example we just have controllers and then we have the map default controller route I'm probably not going to be running the application or I will probably run it but the main point of mediator is being able to segregate logic and in one specific class so imagine you have let's say a hundred actions in this one controller and all of these actions have some business logic in them and what mediator aims to do is basically take that business logic out and put it in a separate so the way we basically do this as we take this business logic and let's put it out in that class that is meant to get triggered right so I have a second project because this is essentially what mediator allows you to do if you're basically putting all your business logic be in these mediator handlers we can do is then grab this project and move it between applications right all you have to do is implement mediator in that application and you have access to all the commands and that's essentially the part that basically knows what to trigger based on what you want for the home controller let's say that this is something that retrieves information from the database so let's pin this solution Explorer let's go to the services and let's say that we're working with cars okay so cars are the car is our model right so let's go ahead and make a models folder and in here I'll just have a car CS just so it's something to work with right so name it doesn't need much more than that close that and then for the services I will have something along the lines of cars so these are car services and I will have queries and let's say get all cars let's see yes okay so they get all cars I will also actually should have named it query so let's go ahead and rename this to query so this is a query this is something that's going to be used by mediator in order to trigger a call to the handler okay so there are two primary concepts that you need to be aware of mmm maybe three let's say you have the mediator the thing in the middle that sort of dispatches all the stuff around the commander query which is basically just a message which lets the mediator know which handler to trigger right we have the mediator commander query and then the handler okay and the mediator set decides which handler to pick based on the query or commands and inside the box of mediator all it does is just uses some reflection to figure out what to surface as the handler okay and I'd use it works alongside your dependency injection container so without rambling on for too long what you tend to do is in your services project you add the mediator project so manage nugget packages look for mediate are let's go ahead and install this once this is installed once this package is installed what you then get access to is the I request interface and will be important using mediator and if you want to return something or rather the return type so you're essentially this is the commander query and this is what it's going to return so in our cases we get all cars so logically this should return some sort of a list of cars so let's say ienumerable of car okay and let's import this from models and the way that you specify a handler and I like to do these in the same file so I have a query so this is the trigger and then I will have the public class yeah all cars query and that will just append append handler to it and then I will inherit from I request handler this will accept two things the information coming in and information going out so again a quick press f12 and we navigate to the request handler we will see that it accepts from the I request handler of T request and unit so this is a version as that is meant to return nothing so unit is the thing that you return when you don't need to return anything so kind of like an empty object but in our case the thing that's coming in is the get old cars query and the thing is that is going out is the enumerable of car okay let's go ahead and get this class implement the interface so at this point you have the handle this is what's gonna get triggered by mediator and this is what's going to return some cars right so let's go ahead and make this or let's not make this thing and say okay synchronous but let's return tasks from result and we'll just make a new car array where we can just return some sample cars so a new car and let's say name I don't know what's the coolest card our Ford and the second coolest card Toyota I don't know what people drive these days this is a car array that let's go ahead and see what it's crying about so it seems like there is some typing uh you know what I had a good date today let's not ruin it with silly errors right yeah I just have a handle where I return some information from if I need to do my dependency injection I can I just great create a constructor and do dependency injection here if needed right so if we need to inject the database or whatnot we do it here okay so we got this what do we do next how do we actually trigger it well remember the three parts we have the commander query in our case we have a query right here third part is the handler the thing is that the mediator is gonna invoke and the first part that we need is the mediator itself right the thing that is gonna do the triggering so to actually register the mediator with your Web API is in your way baby API you will need to again manage nukid packages you will need to browse for mediator and in here you will look for mediator doll extensions dope makers of dependency injection and this is the part that's gonna let you load mediator in your application okay and basically but in the container scan the assembly for all the handlers and stuff and load them into your dependency injection container which is essentially what mediator does on startup so let's go to startup and let's go ahead and register mediator so add mediator and first you will get squiggly lines so what you want to do is provide the assembly where your handlers live okay so the important bit is the handlers okay so when it registers these handlers it will based on the types that are coming in it will know how to trigger this handler because it has registered it okay so when we do type up we will need to grab any class from the assembly where the handlers live so get all cars clearing because all we need to do is just reach the assembly let's go ahead and import this okay and yeah just to restate as I have a project reference to these services here so minimal setup what you do then is from your controller in your home controller some business logic you go ahead you cut it you go to your query and you move your business logic into this handler here and what you end up with is a constructor where you are going to inject a mediator mediator you're gonna initialize a field okay and then what you're going to do is you're just going to create a task and you're gonna return the thing that the handler is in going to return so car let's import the models and then you're gonna use the mediator and you're going to all send and then a if you are but if you need to populate the query from the route then you do stuff like from route from query from body or whatever and you grab your command here so get all cars query and you have your query and then you pass the query and then this will return the data that you need if you're not populating the query from the body route or query you or anything like that you can just go ahead grab this type and just pass it here okay let's line up right and this will suffice alright so here you can say that we are going to be providing some type of an I request and then it's going to return that return type that we specify for the I request right so we're returning this and so that's pretty much the get old cars we can go ahead and name this an API controller we can set the route to ours and since this is going to be the index so we can call it HTTP GET don't need that bit and this should work okay so let's go ahead and launch this and we'll do a little test so here I'm gonna grab the URL and actually already opened up the browser for me I'm gonna go to slash cars and here I'm gonna get an array of my cars okay so I don't know this this example doesn't get simpler than this three parts as I've stated before you have the imediator you have the handler and then you have the request and yeah all you do is you just add the mediator here you don't need anything else to get started another thing I'm gonna show you is basically the command part so queries are when you have get statements if you have post for create but for update and delete for delete what you're going to do is for your cars you're gonna create a folder called commands and then here you're going to say create car command right and from there at that point on you just know where you already know what to expect right so you have services you're gonna have some logic that is associated to cars and then right do you want to get information about cars or do you want to do something with cars so or the create car command you do the exact same thing you do I request and then whatever you want to return maybe some kind of message and I'm gonna show you what I do with this response type in just a second or what I like to do so and again the same thing I keep these in the same file because basically then it just has the command and I know but if the handler is just gonna be where the command is so I don't I'm not gonna have a file explosion it's gonna be easier to read and find what I'm looking for okay so great command handler let's inherit from I request handler is the thing that's going in is create car command the thing that's going out is the string create the body implement the handler and I'll throw not implement I'm actually going to implement this because the story is pretty much the same we go into the home controller we create a post handle we are going to be posting some case in this is gonna be a create car command all that command and send it to the mediator right that's pretty much end of story or the mediator but then you basically as you're using it more and more there is gonna be thing things that you find that you can basically simplify or abstract or the things that you want to do or would would have rather known in the first place before you start it okay and this is what I'm gonna tell you about now so you don't so you can avoid some common pitfalls right all right not not even pitfalls just not not impact basically have a positive impact on your programming experience okay okay so first of all let's go ahead and create a pipe okay so a pipe for mediator is like the middleware and it's just some stuff that you can do to your request and the response when it's coming in and going out right so how we have the middleware for espenak or the pipe is middleware for the mediator okay so let's go ahead bring this end and i'm just gonna put it in the infrastructure here so i'll have it i would have an infrastructure folder and I would have this is something I implement quite a bit with my API so user ID pipe and you will have to inherit from by I pipeline behavior so this is the mediator interface that will essentially say do something with the message that's coming in so we don't have to specify any concrete types so T and T out stuff that's coming in and stuff that's going out so well the basically the mediator will handle these types when sending messages about okay the primary thing that we have here is the thing that we want to do with the message and if you know how middleware it works if you don't I'll link the link you have an annotation to the video right so this is essentially middleware if you know how middleware works you know what to do here and you know the power of this and hopefully I will be able to demonstrate you this so what you will have with your API is something like a access token coming in and what you want is to read the user ID so what you would usually do is you have the user ID living on the user object here with the claims and then what you would have to do is grab my claim and they would have to grab the command and will have to get the user ID and I will have to assign it to the claim and then there might be other stuff that you need to do and before you know what your controllers are again okay so what you do is you go into your startup you say right I want to expect expose the HTTP context to my services so we're gonna add HTTP context accessor I think that's the function yeah and what we can do now is in our user ID pipe we can create a constructor and we can say that we want the HTTP context accessor let's grab the access and then we'll grab the HTTP context from the accessor slide up make sure we create a field okay so we have the HTTP context we can go ahead and grab the claim that we need so HTTP context user claims first or default and let's say we want to the type that equals to claim types that is the name identifier right so we're grabbing the user ID okay and yeah user ID let's not forget to select the value and yeah since the user ID let's how do we attach it to the command so we need to know that the command of a certain type so we can actually attach the user ID to it so what I tend to do is I would have something like a base request okay so base request and on this base request I have some base information that I would like to populate on my requests okay so I would have string user ID okay and then for my queries I will just do this here for the get all cars since we got it working so it's basically what I want to do and I will just add the base request here but a comma and this should work what will happen now in the user pipe so on the base request we just put the user ID then you put the base request on any of your queries or the commands where you expect to have a UA what's called a user ID and then in your pipe I'm not gonna put this user ID but because I don't have any users attached what we can do is we can see if the request is a base request and that we will need to import I can say BR and then for the base a request the user ID I can say whoop right that's my user ID and then we can go ahead and call next and if there is anything else you need to do you do it here if you you probably want to await on this and make this a sink if you want to do something after I don't and you can apply the same pattern with the responses and I do that exact thing the this also this user pipe north when traders example shows you how to implement a try-catch and they think a time pipe which basically times your request if your request is too long log it and then your developers know what owed path is slow but yeah if you don't need to do anything after just return the next and you should be good so yeah so let's go ahead and watch this or see this happen somehow we can go to the old cars query and we can just to the Ford let's go ahead and append the what's a cold request user ID okay let's go ahead and run this and oh I forgot to actually add the pipe right it's telling me all you need to do to add the pipe is you go to the startup and that you go to services ad scope and you will need to provide type off because it's a generic so it's a little bit weird how you add this pipe so let's go ahead I can't I can never remember kind of the same way how you add the other services but just grab the interface put it in first remove the generics and make sure you have the comma there and then for the second parameter you want to do the same type of and then you want to grab the class again go back to the startup paste it here and again just remove the generics and make sure that you leave the comma in there then we want to import this okay so we essentially implemented a generic interface with the generic implementation and mediator will automatically handle grabbing this pipe and by the way if you have multiple pipes the order that you add them in right they will be executed in that order so top to bottom first to last okay and they're the same how you add your middleware and the order in which you add these pipes that's the order in which they're going to be executed okay but I'm only gonna have a one pipe here let's go ahead run this and let's take a look okay so let's go to cars so it looks like I have passed an HTTP context accessor instead of an eye HTTP context accessor my bad rookie mistakes still happen all the time you're never gonna run away from them so yeah get the cars object reference not set to an instance of an object obviously just that just a hindrance at this point I'll leave this in because this is actually production-ready code but rather we just don't have a user in there okay so third time's lucky let's go I will always do these tutorials no prep just tell them tell it how I know it right yeah anyway we have the ID right so the pipe is working if you have users signed in you would automatically put some properties okay and there is other stuff you can put caching in these pipes you can grab stuff for a stuff from the database you can do entity framework transactions so you can mark your queries as iqueryable and/or I query that will inherit from I request and you can check the type here if it is an I query you can go ahead and disable change tracking on your entity framework if it's an I command which is again inheriting from I request before that you can put your what's called handlers and transactions inside these so there is a lot of things that you can do here another thing that I want to talk about is the response again how I handle it so let's go ahead close all of this for the response let's go ahead and create the response class so the response class for me is usually static so the primary thing that I'm after is a response class that will be of some type so it's going to return something a couple of things that I'm after is just the type and the data that I'm returning so T data and then I will have stuff like prop string at strength maybe the big tip message and I would have prop bulb error right so this is my go to here's where I returned my data so let's say I would send a cam send a request to create a car what I would do is return the data for the create a car and the messages I would say car is created by neat translations I don't need to handle the translations inside my spa I can have all the translation on the services and if I'm moving my spot to something else I don't need to redo the translations or all the translations can be done on the service so the message that's why the message is there and then the bull error again you can then write a service on your front end to sort of expect a response like this and know how to handle it automatically so I think it's important to have this wrapper on your data that you're returning so you can basically then can then you can have middleware on your spa to sort of handle the responses from your API in a more graceful way ok so let's go ahead and take a look at how would we return this right so let's go ahead and yeah in here well create a car let's go ahead and say that we will return the response of type car so we're gonna create a car we're gonna save it to the database etc and we would have to change the type that we're returning so instead of string we're now returning a response car we're gonna do the same thing here so let's say if false if something fails right if car already exists if you don't have permissions to create cars what you do is you implement a function on your response and you'll do something along the lines of public static response tea fail tea and then you say a new response you create a constructor right where you have tea data string message and you have bull error and you assign all this data so data equals data message equals message and error equals error and then for the fail you would accept some T data and then string message and T data can be no so we can do something along the lines of this so pass it second and pass a default because it can be an integer so we can just add a default here and when we do create a new response we can just say that right pass the data pass the message and just say that the result is false and then if you need the okay or a good response you kind of go here okay so okay or T data now I sort of I try to pass this first here and by the way for those pipes you can also put validation in there with fluent validation but validators or the built-in Microsoft validation or rather not Microsoft is being a core validation and I think the Northwind traders has that example but yeah they're the actually know the error here is false the error here is true right so what you do in your command is you would say response bail for car and we would say already exists right and that's pretty much it would it's a I guess we would need to return it from tasks from results okay something like this obviously it's never gonna execute otherwise we just say affirm result response okay and we would say a new car what name could we do I don't know Mazda and would say car create it okay so this would be a generic response is the struggling with a parameter that I put in the wrong place yeah okay nice so yeah this pretty much wrapped all this data the returned in the response object so then you can basically handle those responses on your back-end right or sorry your front end rather so let's go ahead and see what this looks like let's see what the error is okay so yes so no longer is returning this we are returning response of Type R yeah I should be good so let's start this let's start a postman so we can post something I don't think I can do it for the browser so here we are let's Ram it let's put the URL post it send it and we got nothing obviously as we want to post a car's still nothing the cars controller surely oh there we go so unsupported media type because I'm not passing anything so let's say body raw JSON empty object let's send it and here we have it right let me create a new car Mazda car created air false you get the gist right and now that we returned this data we can have an interceptor has there been there an error if nope should do proceed if you there is an error we can display a pop-up there's been an error right or initiate retries etc right so it could become cumbersome to basically have these responses and basically having this response in the I request handler so you can always have your rappers so let's have the rappers right rappers folder request rapper again this is gonna be an interface let's create this this is gonna inherit from I request we're gonna have a team and this is going to be a response of team right and I have a team okay so as you can see we're just basically proxying so now we're we're booking I don't reppin it up to basically have a little bit of an either approach to this and that we do the same for the I request handler so we copy this we put this here as a reference and then we can create a public interface I and leur rapper and let's say T in T out and this inherits from I request T incomes in the out goes out okay and think that's it am I missing some brackets here yep and I'm probably missing some types where TN is let's check where yeah where T request is to give response yeah yeah right here we could rather say something along the lines of where T in inherits from I request rapper T out okay so now we're sort of crystallizing our extension of the mediator to sort of use these response type so now if we go to create car command so we can have a request rapper and instead we have a car and we have a antler wrapper and instead of response we also just have a car okay but we still return response car it's just it just becomes neater interfacing and by the way just as I mentioned mentioned in the pipe what you can do if I bring up the user ID pipe let's say we have the results so result equals a weight next if we can make this a sink at the end let's go ahead and return the result here we can do if results is response of some type I don't know car let's bring this in and let's say the car responds right we can do results or let's say fold up car our response let's say the data is going to be a car because we know it and we can say that the name let's append to it right so we checked it cool yeah let's go ahead and try to run this make sure everything still works okay so here's postman let's post again and you will see is the response that we get has been in the checked by the pipe so again if you if you need to do something with your request that it's coming in make sure to inherit from a specific type or you can check for a specific type in your pipes and when you return the response again you can do so through your pipe and as I demonstrated by surfacing the HTTP context and it may be if you need to surface the database or some cache you can do it in your pipes as well hey but this is essentially it so real three parts as a mediator itself the thing that sends stuff around the command or the query it's essentially the message that triggers the third part the handler and then you have the pipes which is really just middleware okay and this is basically the base request response is how I personally interact with the requests coming in and the responses coming out if I need to interact with them I try to bunch them up the response is more important well based request is really important for the user ID I find that to be the main use case the response the main use case is for it to be able to be handled gracefully by the front-end ok and yeah another thing is just you just have a service folder for whatever entity you're working with or the domain area and then you have the commands for that you have the queries for that and I keep my commands and queries along with handlers in a single file it kind of keeps the logic together right and by the way if you need to send another command from the handler to another you can dependency inject mediator into your Handler and then you can issue another command from one handle to another ok there's also a fire-and-forget I'm not gonna touch on it too much because I've never had the need to use it you have the mediator publish so what this will do is it will send a notification to a bunch of handlers and I think it should act as a fire-and-forget sort of thing but again I've never used it so I don't think you will have a need for that for it either yeah this is essentially for this episode thanks for watching if you enjoyed it leave a like subscribe if you still have any questions make sure to ask them in the comment section code is in the description I also stream on Wednesdays and Sundays where you get to see me use technologies like mediator so make sure to join my discord channel if you would like me to update you on when I stream or follow me on Twitter yeah as always hoped seeing my other episodes
Info
Channel: Raw Coding
Views: 39,913
Rating: 4.9460826 out of 5
Keywords: c#, .net core, asp.net core, tutorial, explained, overview, example, mediatr, cqrs, tips, how to, use
Id: xKKVW94F2bc
Channel Id: undefined
Length: 39min 10sec (2350 seconds)
Published: Mon Jun 01 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.