Clean Architecture With Document Database, Minimal APIs, And CQRS In .NET 7

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
how do you apply the clean architecture with document databases in this video I'm going to show you how you can implement the clean architecture with Martin which is a document database based on top of postgres we're also going to use minimal apis for our endpoints and we're going to use zqrs with mediator for the application layer I'm first going to show you what I prepared beforehand and then I'm going to start with the actual implementation the first project that I have is the web API which is going to be our executable application it's just a simple.net 7 minimal API with no additional dependencies then I have the presentation layer and this is where we are going to Define our minimal API endpoints then I've got the application layer which already has mediator installed and a few dependencies defined we have the command and query Handler interfaces and I added some interfaces for registering service lifetimes and lastly we have the domain layer where I added a few abstractions for implementing domain-driven design tactical patterns such as aggregate routes domain events and value objects we're going to start from the domain layer and we are quickly going to have a working implementation since we are building an eShop application the first entity that I'm going to add is a product the approach I'm going to use for grouping our entities is going to be to group by features instead of by types so we're not going to have an entities folder we're going to have a products folder and inside of it we're going to define the product entity and any related abstractions that we're going to add in the future videos so let's add the class that's going to represent our actual product entity I'm not going to complicate the implementation I'm just going to add a few properties to our product and then we're going to see how we can use it in the other layers in the clean architecture the first property is going to be an ID and I'm going to be using a 64-bit integer for example I'm going to give it together and as header let's define a name property now and we can assign it an empty string value because we're using nullable reference types let's give it a price for that let's use a decimal and I said I want to be able to add tags to the product so I'm going to define a list of strings and this is going to represent my text and let's initialize it to an empty list so this is going to be our product entity I mentioned we are going to use a document database based on postgres for persisting our entities so we are able to do things like this like persisting a collection of strings on the entity which is a little difficult to do with relational databases where you would have to define a separate table for storing the list of tags now that we have our product entity let's go up to the application layer and Define a feature folder that is going to hold the commands and queries for the product so I'm going to name this feature folder product and I'm going to start by creating the First Command which is going to be our create product command so I'm going to name the folder create product I first need to define the create product command so let's go ahead and do that I'm going to make it into a record and it's going to implement the I command interface so let me first make it into a record and let's implement the I command interface so I command and what do we need to create our product we need the name of the product we need the price and a list of tags so let me add those name and we need to add the price and we also need a list of shrinks which is going to represent our tags so this is our create product command definition and I'm going to right away go ahead and add the create product command Handler we're going to implement the I command Handler interface and we're going to handle the create product command so let me add the interface and the create product command so let's go ahead and implement this interface what I want to do in the handle method is create a new instance of the product entity and persist it to the database so let's add our first library to the application project I'm going to look for a nougat package and the one that I need is called Martin which is our document database which is based on top of postgresql let's go ahead and install the latest version of Martin inside of the application project and let's go back to our create product command Handler what Martin gives us is the ability to work with an eye document session so I'm going to go ahead and inject that inside of the command Handler so I document session this is coming from Martin let's give it the name of session let's inject the document session from The Constructor the document session is going to allow us to work with the database it's basically the same concept as the LED framework database context and we're going to use it to persist our product entity so let's go ahead and implement the handle method using the document session that we just injected so I first need to create a new instance of a product so I'm going to instantiate a new product and let's assign the name price and text properties that we have on the create product command let's assign the product name which is going to come from the command and also the price and lastly let's assign the tags to the request Tags I'm not going to be giving a value to the ID Martin is going to take care of that for us when it persists The Entity to the database let's make the handle method asynchronous because I want to use the async methods on the document session so what we need to do is access the session and we just call the store method and specify the entity that we want to store in this case we're only storing our product and this is everything that you need to do to work with Martin there is no entity configuration there's no mappings you just take the entity store it using the session and Martin is going to take care of serializing the entity into Json and persisting it inside of postgres to process the entity inside of the database we need to call session save changes async and this is going to store the product in the products table in the database so so this pretty much completes our handle method so I'm just going to return the success result and finish the implementation so this takes care of creating the product and persisting it to the database using Martin now we're going to go to the presentation layer and create our minimal API endpoint I'm going to add another library to the presentation layer that's going to help us in defining our minimal API endpoints and this library is called Carter Carter gives us a set of extension methods for defining our minimal API endpoints and it's just going to make our life that much easier inside of the presentation project I'm going to add a module which is going to hold the minimal API endpoints for the products route so let's define it as products module and what we have to do with the products module is implement the icars or module interface which is coming from the Carter library that I just installed so I Carter module and you're going to see that this interface only has one method which is as routes and we're going to use this method to Define our minimal API routes so the way that you define a minimal API endpoint is by calling the I endpoint route Builder and on it we have the map post method we're going to use the map post method to Define an endpoint for creating a new product I'm going to give it a route of products and we're going to define a delegate or just a Lambda method to represent the body of our endpoint so let's make it an empty Delegate for now and let's think about what we actually want to do inside of this endpoint I want to instantiate a create product command then send it using mediator and then return some HTTP status code as a result so let's Define a simple request object that we are going to map to our create product command and then we're going to send it using mediator so here's a simple request object which is called create product request I'm going to specify it as the first argument that I'm passing to map post I'm going to give it a name of request and I want to have another argument which is going to be our mediator sender so let's format this a little differently and I said I wanted to add the I sender which is coming from mediator so let me just import the mediator namespace so let's define the body of our map post method I'm going to create a new create product command I'm going to map the create product request into the command by calling adapt which is coming from the map store library and I just specify the create product command as the type now I want to send my command using mediator and since mediator has an asynchronous interface I'm going to make this an asynchronous Lambda method now we can await the sender.send and specify the command since this returns a result we may want to instantiate the result into a variable and check if it is a success or failure result but for simplicity's sake I'm not going to do that right now let's just return an OK result to specify that we have created the product successfully so I'm going to say return results dot OK and this is how you return a 200 okay result in a minimal API endpoint and what's left to do is configure the services required in our web API project so let's go ahead and do that I'm going to add these Services required by Carter so we call Builder services at Carter the second thing that you have to do to configure cutter is called app dot map Carter and this takes care of configuring our minimal API endpoints and let's also Define what's needed for Martin to function with postgresql so I need to call builder.services add Martin and I need to specify the connection string that's going to be used for connecting to our posters database I call options connection and I need to specify which connection string to use for that I'm going to use Builder configuration get connection string and the name of the connection string setting in my app settings.json is just database so this takes care of configuring Martin which is our document database based on top of postgres I'm not going to do a deep dive of Martin in this video but just know that it's going to automatically take care of configuring the required tables in the database for storing our product entity mmead entities in the future it's going to automatically create the required tables I prepared a simple Docker compose project and we're going to take a look at the docker compose yaml file you can see I just have two Services one is for our web API and the other is for running postgres inside of a container I can just start Docker on my machine run the application using Docker compose and I don't have to worry about instantiating the required Services which is postgres in this example one more thing that's left to do is to configure mediator so let's go ahead and do that I'm going to call builder.services.admediator and we need to pass the application assembly which is where our Command and query Handler are defined so I'm going to access the application assembly and access the instance on it and this should take care of configuring mediator so let's start the application with Docker compose and try to persist our product as you can see we get the Swagger UI and we have one endpoint which is our post products endpoint and let's use it to Define our first product for example let's create a ryzen CPU let's give with a price of I don't know a hundred dollars and let's give it a few tags one tag can be CPU and another tag can be microchip for example and let's send this request to our API and see if we can hit the breakpoint that I set inside of the create product command Handler we hit the endpoint that I added inside of the create product command Handler so let's instantiate a new product instance let's now Store The Entity using the document session provided by Martin you can see that the ID is set to 1. Martin uses a high low strategy by default when defining an integer or a long ID and it's going to pull a set of keys from the database and it's going to store them in memory and assign them to new entities on the fly so now I call the save changes async on the document session and this is going to persist my product inside of the database you can see this complete successfully so I'm going to press continue and we're going to get the response for our API which is 200 okay so let's quickly Define a get endpoint to fetch all of the products in the database I'm going to add this to the products folder in the application layer let's call this feature get product and let's add the query and the query Handler for getting the products from the database so this is going to be the get products query I'm not going to add any query Arguments for now it's just going to be an empty record so public sealed record is going to inherit from I query and it has to return some sort of result so I'm going to define a product response which we're going to return from our API and we're going to use that as the result of the query we're going to be returning a list of product responses in the get products query so let me Define that and I'm just going to move the product response into its own file so we have the get products query let's add the appropriate query Handler so it's going to be the get products query Handler and let's quickly implement it I'm going to make it sealed and it has to implement the I query Handler interface and I need to tell it which query it's handling it's going to be the get product query and I also need to Define which response we are returning which is going to be a list of product responses so now I can go ahead and implement this interface and we're also going to be using Martin for querying the database but this time I'm going to be using a different kind of session I'm going to use the I query session which is similar to as no tracking with NAD framework so let's inject this session from The Constructor and let's use it in the handle method to fetch our products querying the database with Martin is very simple I'm going to create a variable that's going to hold my products we're going to say session we're going to call the query method and specify which table in the database we want to query document databases don't actually have a concept of tables it's called a collection in the document database world but since Martin is based on top of postgres I think it's safe to say tables as well we're going to query the products using this session I'm going to call session query specify product here and we want to project the product into our product response so I'm going to close select and we need to instantiate a new product response and I need to specify the product ID name price and tags so I'm going to just quickly do that so price and tax now that we have selected our product response we just call tool list async and I can pass in the cancellation token and of course we need to await this to materialize it into memory so I'm going to make this type explicit you can see that this actually returns a read-only list of product responses so I'm going to return products and I'm going to call to list just so that I don't have to go back to the query definition and replace this with an I read only list but you can totally do that and I suggest returning read-only collections if you don't want anyone to modify what you return from the query Handler let's quickly go to our products module and Define a get endpoint for getting the products I'm going to add it just above the post endpoint so I'm going to call map get we're going to use the same route of products let's define an async delegate which is going to represent the body of our endpoint we're going to inject the I sender from mediator we're going to use it to send our get products query so I'm going to make this a one-liner I'm going to save results okay and I'm going to await sender dot send and I'm going to instantiate a new get product query and this should take care of sending the get product query and returning a list of products from this endpoint so let's start the application and see if what we have is actually working as you can see we have two endpoints now so let's try our get products endpoint to see if we are getting the product that we created in the database earlier we do indeed get our ryzen CPU that I just created let's also try creating another product and see if it's persisted I'm going to create a new ryzen CPU let's give it a price of 200 and let's update the name to version 2.0 and let's send this request to our database as you can see this completed successfully so if I go back to the get product endpoint and send it again we're going to get back both of our CPUs what's interesting to note is that the ID is going to be a thousand and one I mentioned that Martin uses a high low strategy for generating the IDS and the batch of IDs that it takes from the database is a thousand so if you stop the application and start it again Martin is going to lose the Thousand IDs that it registered in the database so this is why we have the ID of one for this document and 1001 for the second document so this is the clear architecture using a document database with Martin and minimal apis for our endpoints I'm curious to hear what you think about this implementation of the clear architecture so please let me know in the comments don't forget to smash that like button and also subscribe to my channel so that you don't miss any of my future videos and until next time stay awesome
Info
Channel: Milan Jovanović
Views: 23,379
Rating: undefined out of 5
Keywords: clean architecture, clean architecture explained, clean architecture .net, clean architecture cqrs, clean architecture mediatr, clean architecture marten, clean architecture minimal apis, minimal apis, marten, document database, postgresql, marten postgresql, clean architecture postgresql, clean architecture .net 6, clean architecture .net 7, clean architecture minimal api, clean architecture document database, clean architecture .net core, .net architecture, cqrs, mediatr, mongodb
Id: Ru6_b50wdfo
Channel Id: undefined
Length: 17min 42sec (1062 seconds)
Published: Fri Dec 30 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.