Build Messaging in .NET with Wolverine

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello everybody I'm Nick and in this video I'm going to show you how you can get started with messaging in net using a inid package called Wolverine and when I say messaging I'm referring to asynchronous messaging so imagine a q or a pub sub topic scenario now in this video I'm going to focus mostly on cues and everything you're going to see now is applicable both to service service communication so service a pushes a message to a que and then service B picks it up but also within the same service so if for example a user is registered into your service and you want to fire an email later maybe from the same service if it is a monolith or a modular monolith you can publish something in the queue and something within that service picks it up from the queue not only that but this Library also has a mediator type functionality for inmemory message processing so we're going to see all that in this video we're going to see how we can use it with multiple other providers for example rabbit mq and also AWS sqs but everything you'll see in this video would also be possible with the exact same code with other services like Kafka or something like azus service bus big thank you to AWS for sponsoring this video if you like of content and you want to see more make sure you subscribe for more training check out my course is on do train.com okay so let me show you what I have here and by the way Wolverine you should think of it as a library that comes into the picture similar to how n service bus or something like M Transit would come into the picture for your application it just tries to abstract all the nitty-gritty of messaging and gives you a nice interface a nice developer experience for you to go ahead and publish messages and consume them that's all there is to it so I'm going to go to this Wolverine demo over here and I'm going to show you how easy it is to get started that's the one thing that I like about Wolverine is how quickly you can hit the ground running but also one of the things I kind of don't like about Wolverine is how opinionated it is about how it does things for example Wolverine when you say use Wolverine we're going to see that in a second we actually replaced the built-in di container with Lamar which is from the same Creator the Creator seems to be very opinionated I don't like many of those opinions but what I see with Wolverine seems to be more positives than negatives so I'm happy recommending it now before I move on I'd like to let you know we just launched a brand new course on dome train called getting started with modular monoliths in net and is authored by the legend Steve oralis Smith I'm sure Steve has taught many of you already with courses on other platforms like PR but now he authored his first of many courses on dome train and it's all about how to get started with modular monoliths not only will he teach you the theory behind the concept and how it compares to microservices or traditional monoliths but he will also build a whole system in that course hands on with code and diagrams and examples you can follow along it is an amazing course and is the best way to get started with modular mon hands down in.net now to celebrate the launch I'd like to offer the first 500 of you a 20% discount account so either use a link in the description or apply code modular at checkout to claim the 20% off it's a great opportunity to get started with a concept and I can't stress enough how much of an amazing author Steve is now back to the video so let's first have an application Builder over here so I'm going to say host. create default Builder and if you're wondering where this is coming from I've already installed a new get package here called microsoft. extensions. hosting which allows me to create applications like this this is not a web app this is just an application with a host so then similar to how you'd have a minimal API approach you have the app so Builder do build that gives you back the app and then you can say app. run and that's about it now what I'm going to do is show you how easy it is to use Wolverine so first I'm going to say Wolverine FX and I'm going to add the N package so that's the core Wolverine package and by default it doesn't have any transports or means of transportation of your messages other than the built-in one which is an inmemory version now how do you use Wolverine well simply you say Builder dot use Wolverine and that's about it by default it just configures all the defaults of course and it has the inmemory processing built in now what I'm going to do is I'm going to create a message something I'm going to be publishing and in this case I'm going to have a message called create customer so I'm going to say good as the ID a string as the full name so in this case this message is written in a way that is saying that I want to do something I'm commanding I'm ordering for an operation it's sort of imperative create a customer do something when you have that you're sort of standing in point a and you're saying that's what I want to do and at some point later in the pipeline point B will pick up that message and do something about it in this case hopefully create the customer you can also have the other approach where you say customer created and you publish an event and you announce it and you say hey that happened and if anyone is interested they can pick it up and you can have this sort of multiple consumer scenario where when a customer is created one service is putting something in the analytics but another service sending an email but another service is scheduling a birthday reminder email or something along those lines here we're going to start with the I'm service a or endpoint a and I'm gonna say something to endpoint B as a message using a q mechanism now for my publishing I'm going to create a background um service and that's going to be a background publisher so something in the background will be publishing and I'm going to have that be an i background service which will have an execute a sync and then I'm going to have a loop that every two seconds publishes a message now to publish a message using Wolverine you'll have to inject the private read only I message bus so I'm going to say message bus here and inject it from the The Constructor and then I'm going to have my Loop so while the stopping token is not cancellation requested then have a two second delay so I'm going to say a task. delay for two seconds of course I'm going to have to turn this one to async and also pass the cancellation token so I can gracefully cancel my application if I want to and then I'm going to say message bus. send a sync I'm sending I'm not publishing I'm saying this goes somewhere and then I'm going to have my message that create C and I'm going to pass just the good so new good and then the full name will be Nick chapsas and that's about it so I'm going to say a wait here and fix my spelling mistake on my name and that's about it that's all you need to do to publish a message now you might be looking at this especially if you have experience with other messaging approaches or messaging libraries and you say wait this doesn't Implement IM message this is just a record this this is just a class and yeah that's all you need in Wolverine you don't need to implement an interface or a class you can just have the plain old C object and that will be picked up automatically by Wolverine based on convention and potentially further configuration if you need to but the class itself doesn't need explicitly anything else you don't need any new get packages and that means you can take that object and put it in a messaging contract library that you share within your team and that doesn't need to have a wolverine dependenc it can just be like any other contract libr and what's interesting is that obviously now we are publishing something but we need to consume it as well the consumers follow very much a similar approach so you can say create customer Handler and that will handle that message so if I need to consume it all I need to say is public async task handle and then pass down the type I want to handle so create customer and that is it now obviously this needs to be public to be handled so I'm going to turn it into public but yeah that's all there is to it it doesn't need to be I Handler it doesn't need to be anything beyond that which is both cool and also a bit scary for some people some people like naming convention and sort of duck typing some people don't like that cop and net in general has both we both have duck typing think of middleware for example yes you can have the I middleware interface but you can also have it based on convention so because it sort of exists in the library and the CL I'm not too mad about it in general I prefer building on a contract because it makes me feel safer but I want to know what you think because there is actually a reason why this is the case and why we can have this and the reason for that is because you can actually have multiple handle methods for different types in a single class which is pretty neat but I wouldn't do it because I want each class to be handling a message in a very specific way so now that we have this let's say we want to inject a service to print the message in uh the console log so I'm going to say logger and I'm going to say create customer Handler so di will be working I'm going to inject the logger and all I'm going to say is logger.log information and this is not how you should be doing logging but just for argument sake I'm going to say create customer dot to string don't do this if you want to do this you should break down the parameters or use the add symbol to deconstruct the object but the reason why I'm doing this is to treat this as a console log um but showing you how you can inject a service and actually I can have this be an I log or why not use the interface instead and that's it and just by doing that of course this doesn't even need to be a sync task but it can it can also be void as you can see and that's all there is to it so all I need to do now is register my uh service the the hosted service so I'm going to say x and then add hosted service that's the background publisher that's going to be be publishing a message every two seconds so let's do that and then that's all I need if I just say run what you're going to see is I'm going to be publishing a message every two seconds and then what you'll see is successfully process messaged from that local queue that create customer run so this is happening now every two seconds and you can see my console moving and that's minimal configuration nothing really you can just think of this in a way as a mediator replacement the in memory version of messaging but the great thing about this is you can take the exact same logic and expand it to use different transports now there's a few things I want to show you which are pretty neat for example you don't need to inject from The Constructor on your Handler what you can do instead is you can just comment all this out or just delete it in your case you can turn this into a static method and you can simply uh pass it down here and what's going to happen is Wolverine will understand that this service is coming from the DI cont container so if I just do that and I say run we will still work as we used to work before and the service is injected from the DI container successfully I didn't have to do it from the Constructor some people like this some people don't like this the reasoning behind it is because minimal API kind of do this already it goes with the same flow where you support both the Constructor but you also support uh the method injection as well completely up to you but it me you can have this as static and the idea is your handle methods should be pure meaning that handling a specific set of requirements for example the create customer or any other parameter should deterministically produce the same results without any side effects is possible that will give you a very safe very secure and very clean way of handling your messages now just because it's probably what you're mostly used to I'm going to go back to the Constructor injection but you can choose to use whichever one of the choices you want and documentation of Wolverine is pretty good so if you need other ways to play around it I'm going to link documentation down below if you want to check any other Niche use cases as well now that is great and all and the inmemory stuff work fine but let's take it up a notch and let's go ahead and wire everything up on a real application and in fact we're going to have two real applications we're going to have a movies API which will use an inmemory database to allow me to create movies get movies delete movies and update them as well but also have a movies consumer that can do something retroactively when a movie is added or deleted so give you more of a better representation of what you would have probably with a microservices scenario so I'll go ahead and delete everything and then what I'm going to do before I add Wolverine is show you what we're adding Wolverine into so let's go ahead and run this API go to insomnia and as you can see I can go here and I can say create a movie the movie I'm going to create is the classic Nick the Greek from 1993 it's a comedy of course so I'm going to say create and as you can see see the movie was created I can say get all movies if I sent I can get all the movies I can say get a movie by ID and if it doesn't exist I'm getting a 404 but if I pass down the right good and it does exist I'm getting the movie I can update it and then I can delete it so for now what I want to do is when I create a movie I also want to publish a message now there's sort of two trends of thought here I'm going to talk about both of them and I'm going to show actually both of them because Wolverine has another very neat feature so I'm going to go ahead and stop this so I'm going to go to the new packages and say Wolverine fs and just install that for now on the API and then go to the program.cs and say builder. host. use Wolverine so I'm going to go ahead and wind that up in here and for now I'm just going to leave this as it is and then I'm going to go the program.cs over here in this other service which in this case it's a web application but it doesn't have to be the reason why I have that is because usually I expose health checks through my background service Runners and it's very easy to have just a health end point and a metrics end point on that if it's a obligation it doesn't have to be but it can and I'm going to also add Wolverine there as well so I'm going to do that and say use Wolverine and that's about it by default now let's focus on the API for a second the first thing I want to do is simulate how I would use this imperative approach to create a movie asynchronously now I'm sure you've seen in some scenarios in some apis when you say do something create a resource in the system where the API doesn't return to one created but it returns 202 accepted meaning I've accepted your request and I'm going to do something about it now you have two options when you have something like this in your application one of them is to go to the program.cs where I have all my endpoints and let's say I want to create my movie and then go to the movie service and in the create method of the movie service I'm going to say private readon I message bus and I'm going to inject that in the movie service and after the thing was created over here so the thing was created I'm going to say hey publish this message or send this message to the bus but this is more of a publishing act this is more of me announcing that something happened I'm not commanding for something to happen so if I was to use this here I would say publish a SN that a customer or a movie in this case was created asynchronously and something else later will pick it up and do something about it the alternative that some other people do is that they inject the service in here the message bus service and then they just send the act of hey create a movie from here then they quickly return accepted and that movie can then go ahead and be created asynchronously how you want to do this is completely up to you and your requirements what I'm going to do for the remainder of this video just to to show you both approaches is in here I'm going to say movie created but I'm going to just send that message async to some other queue the queue that will have all the movie actions now from a resilience perspective be very careful because usually what happens especially here is a movie could be created in your API over here but the message might fail to send so what do you do well Wolverine has a retry mechanism and you can lean in that and you can let it ret your message as well to be republished in the queue as it can but this is always a bit tricky which is usually why nowadays we see way and way more a situation where you save in the database and then the database itself has a triggering mechanism to publish that message we see that with things like a cosmos DB or a WS Dynamo DB where you can listen to changes on the respective change feeds and then maybe with a Lambda put a message in a q for for the processing I don't want to make this over really complicated so I'm going to show this in a simplified version just so you understand the tooling but know that in production in reality there's way more that has to do with resilience in these scenarios so now I can send a message somewhere but I also want to consume it so I'm going to go to the consumer over here and I'm going to say let's go ahead and create a handlers directory and in there I'm going to have the create movie or the movie created Handler so this will have a public async askk method called handle and I'm going to have the movie created contract over here which I have shared in a library over here so both API and consumer are using it now I don't want to do much here so all I'm going to do again is just print in the console so I logger movie created H Handler goes here logger inject that and just print it in the console again never do logging like this never just say say two string destructure it and store it properly I'm just doing this for demo purposes or again this could be void since we don't have anything here that needs to be asynchronous and that's how simple for in memory stuff this is but we can't really do this in memory because now we have two different Services we have the API and we have the consumer so we kind of need a means of transportation for those messages now and what we're going to use in this case is rabbit mq to start with so rabbit mq is an open source and extremely popular message broker which is implementing the amqp protocol alongside some ARS now but fundamentally originally that was all it was about and you can just run it locally using Docker if you want or have a hosted version if you get the source code from the description down below you're going to have a Docker compos file alongside all this code uh to run it locally if you need to so in my case I'm just going to say Docker compose up over here and that very quickly will create a rabbit mq instance over here not only that but I'm also going to get a interface to examine my cues so after you run this if you go to Local Host 15672 you can say guest guest and you can log in and you can see your channels your connections your cues your exchanges and so on you don't need to know everything about this but as you can see we have some default exchanges we have no cues by default and we're going to let Wolverine manage all that for us how well with a rabbit mq specific extension package so first let's configure rabbit mq on the API layer so I'm going to go to packages and find the rabbit mq package over here and install that and once I do that I'm going to go ahead and just use the extension method or the configuration approach over here with this Lambda and say use rabbit mq I want this to publish a message of type movie created and by the way you can also say publish all messages where you're not specific about where each message goes but just to give you a bit more transparency I'm going to say publish message movie created to Rabbit exchange not the C not the topic just the exchange where then the exchange can put it in a que and we're going to configure that as well so I'm going to say movies exchange and then I'm going to configure The Exchange Transportation so I'm going to say exchange type is direct and then exchange dot bind to a q so from The Exchange take it to the que and that Q name is movies Q over here here and this binding key here will be exchange to movies and that's about it for configuration for the publishing again you could do all this and in this case since the queue will be used for all messages and you're only going to publish a movie messages you would also say publish all messages to the movies exchange and then that will take them to the queue and then after that you can say use rabbit mq over here that will have its own configuration all I'm going to say is that the host name is Local Host since we're running it locally using Docker and that's about it for the configuration in here the last thing I'm going to say is autoprovision so automatically provision the resources in rabbitmq and that is Q's exchanges and so on without me having to go manually in rabbitmq and do it myself and once I do that let me show you what happens I'm going to go ahead and just run the API I didn't change anything else in my code the same publishing code is still in place but as you can see a few things happened over here some things were created and if I go to R them Q you're going to see now we have a movies queue over here which has no messages but it has some bindings in this case it is the exchange to movies bind where we get the movies from The Exchange into the queue and we can say go ahead and create this movie and once I create this movie a message is published into rabbit mq through Wolverine so I can go here go to the queue and in the queue you can see I have one message ready now to be consumed from something that is the message just published and of course of course I can say here for example get messages and I can get the message and I can see exactly the payload of the message because the message again is the body of the message but then all that is wrapped in an envelope containing other metadata for example all this is metadata of the envelope now how do I consume this from the consumer extremely easy same logic as before I'm going to go ahead and add the rabbit mq package and I'm going to copy all this configuration I'm not going to use all the configuration um but going to use most of it so let's go ahead and save this I'm going to say that I'm not actually going to publish anything from this service so I'm going to remove all that but what I will say is listen to Rabbit Q so listen to a que with name movies and this won't be needed from uh 2.0 Wolverine but you can also say use for replies over here I'm not going to go too much in depth into that you don't really need to know much about it and once I have this in place as well I can go ahead and I can say run the consumer and presumably we still have one message in the queue so if I go back and I refresh you can still see that message over here nothing acknowledged it and nothing deleted it just getting the message doesn't mean it goes away so I'll go here I'm going to say run the consumer the consumer will run hopefully we find the message and consume it and print it and as you're going to see yes successfully process messaged of that type and you can see that the object was movie created with all of these information fantastic extremely easy extremely simple awesome now one thing I want to point out which I didn't mention earlier robbit mq has this ability to retroactively publish a message if the return type of the handle method is also an object type what do I mean let's go away from this movie stuff and go back to the original demo example so I'm here with a create customer so what I say is I want to create a customer but after I create I want to say that the customer was created so publish this event this action with this details different actions one is do something the other one is something happened so what I can do in the Handler is let me just duplicate this Handler and say customer created Handler now this Handler the customer created will return the void or the nothing and we're going to use the customer created object over here so customer created but on the other one that create customer it's not going to return void anymore no what it's going to do is it's going to say return a new customer created object with the following parameters the create customer ID and the create customer do name so now the handle method has a return type and this can also be a sync task by the way which means that every two seconds now I'm going to first publish the create customer request or action but because I'm returning the customer created and we just quickly stop it as you're going to see I'm both publishing the create customer but I'm also publishing Auto magically this customer created object because I'm just returning it and you can use that to have cascading messaging where one action triggers another action triggers another action very neat very cool you can fall into issues with this approach especially if you got yourself in an accidental Loop or you refer to things that you shouldn't do in different layers but overall I think this is a great feature so keep that in mind this will also work with messaging providers or transport so that's cool I can have Rabbid mq here and now that's all amazing but what if I want to take it in the cloud or what if I want to just use a different transport all together what if I want to move from rabid mq and go to AWS sqs for example how much will my code need to change well not much at all actually because that's the whole point of libraries like Wolverine so what you would do in that case is you would go over here you would remove the Wolverine package and you would add the one you need in this case Amazon sqs which is the queuing provider for AWS and you don't need to change anything but this method to say use Amazon sqs transport as you might imagine and publish all messages won't go to an exchange anymore but instead they're going to go to an sqsq with name movies q and that's it you don't need to change anything else I'm going to go to the toolkit over here the AWS tool kit I'm going to show you I have no cues as well autoprovisioning will still work for each respective type and that is awesome so let me just quickly delete this old using statement we don't need it anymore and of course I need to reconfigure everything in the consumer thankfully extremely easy so add the Amazon sqs approach and remove the Rabid mq1 I don't need that anymore so I'm going to say you go away you also go away and then this turns into listen to sqsq so the naming of methods is consistent noat the transport making it extremely easy to follow so same thing here use sqs transport and you can also have by the way they use sqs transport locally so if you use something like local stack which is a way to run a WS infrastructure locally through an emulator this is amazing this just works out of the box so let's see how that would work I'm going to go ahead and run the API first I haven't changed anything than the configuration and as you can see the queue was created over here and also a dead letter que was created so if a message went ahead and failed for some reason after all the retries it will end up there so in the toolkit as you're going to see now after I refresh I have my two cues they are empty there's no messages to view so what I'm going to do is I'm going to just run the consumer as well in real time that will also use the exact same q and we try to create the queue if it doesn't exist because I said well go ahead and use it so let's go and create the movie and as you're going to see in the consumer the message was published into sqs and then it went to sqs and consumed by my API so easy so simple I didn't need to do anything again I can stop the consumer and I can create a bunch of more movies of course with a different name so let's have the sequel same year let's have another one and let's have another one as well here we go so all those messages are not lost if I go to the toolkit you'll see that they are still here I can see a few messages and the to is a bit delayed but it shows me and I can click here and see the body and I can see everything about the message and if I go and I say now run the consumer the messages still stay there and asly they are processed in my application and as you going to see now all of them were immediately published and any provider you're going to use any transport you're going to use also has transport specific configuration so things like listener count if you want to have five listener competive consumers all that will just work out of the box it is configured based on each provider currently the supported providers for Wolverine are the following so you have things like mqtt Kafka you have SQL Server so you can actually use a database for that you have rabit mq ASU service bus you also have a built-in fluent validation module so if I actually had that over here let me just quickly show you because I think it's pretty neat you can say usual verine but you can also say use fluent validation and you can have validators for your messages and they can be validated before they are sent to the queue very neat very nice I like that I think it's a great idea especially because it's in a separate n package if you don't need this you don't opt into it but if you do need it you do opt into it I think compared to libraries like n service bus for example this approach is way better I actually was thinking of making an N service Bus video but I found the interface so old archaic it felt like I was working with Legacy code so I really hoping service bus actually fixes that as well and if you want to see a video on N service bus please leave a comment down below and let me know I would still do that because I know many people use it but it's is not really an enjoyable experience using it but now I want to know from you what do you think about this and what are you using are you using n service bus man Transit Wolverine something else leave a comment down below and let me know well that's all I had for you for this video thank you very much for watching and as always keep coding
Info
Channel: Nick Chapsas
Views: 58,384
Rating: undefined out of 5
Keywords: Elfocrash, elfo, coding, .netcore, dot net, core, C#, how to code, tutorial, development, software engineering, microsoft, microsoft mvp, .net core, nick chapsas, chapsas, dotnet, .net, .net 7, aws, aws .net, aws on .net, .net on aws, wolverine, mass transit, .net wolverine, rabbitmq, c# wolverine, masstransit, .net mass transit
Id: p1gsK69m94Y
Channel Id: undefined
Length: 30min 37sec (1837 seconds)
Published: Thu Feb 15 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.