EF Core, DDD, and Clean Architecture - Mapping Aggregates to Relational Databases

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
okay so hi everyone I'm Kai here today we're finally looking at anti-famil core and domain driven design I know this is a topic that many of you have been waiting for for a long time a couple of months already so you've been waiting patiently and today it's finally happening so this is a special video in which we're going to cover many topics so we're going to cover also everything you need to know to get started with domain driven design and it's firmacore and then we'll cover the actual steps that you'll need to take to take your Aggregates whatever they are whatever however complex they are and map them to the corresponding relational database tables then we'll see how we can use flu the fluent API in antiformal core to actually do the mapping between the Aggregate and the database then we'll see how we can create a SQL server on a Docker container and use the anti-firmware core CLI tool or dotnet CLI tool to connect to the database and do the various actions so if it's the migrations after dating the database and so on and we'll also use Visual Studio code to explore the database so there's going to be a lot going on we're going to cover a lot of topics this video is going to be dense and intense but by the end of this video if you watch until the end then I think you'll have everything you need to know to use anti-famil core in your applications following domain driven design principles there are some mistakes and nuances that are very important to know if you're working with anti-firmacore and I think that there are many applications that do this wrong and the only reason that they don't break or they don't see these bugs is by chance and tomorrow morning their application might break so even if you're already using anti-frame record in your applications with domain driven design I encourage you to watch until the end and make sure that you're doing it correctly in your application so there's a lot to talk about let's get started first let's understand why it's even a big deal using anti-framal core with domain driven design and why it's not so trivial so the first thing if you've been following my C series of building your rest API completely from scratch following clean architecture and domain driven design principles then you noticed that we haven't talked about the database yet at all this is the first time that we're talking about the database if we're going to use a relational database if we're going to use a document database what database provider we're going to use we haven't talked about this yet and that's because we wanted to follow persistence ignorance which means you don't care about the stored solution while you're defining your domain layer this shouldn't be a concern and that's something that we want to keep in mind okay now there are things that we talked about in domain driven design that we want to enforce when mapping our Aggregates to the database this is something that is done wrong in many applications so let's cover this the first thing is that the identifier of the Egret the aggregate ID needs to be unique within the entire system which means that if we have two Aggregates aggregate one and aggregate two their IDs must be different within the entire system okay moving on to the second building block so that is that the entity needs to be unique within the aggregate so I invented an acronym which is avoid which means aggregate unique identifier so the entity ID needs to be unique within the aggregate so if we're looking at it one One X to the other then if we have two entities then they can have the same ID as long as they're sitting within two separate Aggregates okay so the following is legal and moving on to the third one so value objects don't have an identifier and if we put everything together then we don't want changes to one aggregate to affect other Aggregates and this is where it comes into play because even though in memory we just have reference between objects and ideally we're not deleting entities that the reference of the object is shared between two aggregate objects but when it comes come to the database then if we have for example the following so we have two Aggregates and both of them have inside an entity with the ID of one these are different entities logically even though they have the same entity ID we need to be careful that when we're mapping it to the tables that we don't do the following mistakes so we want to make sure that the ID of the entity is not the primary key of that entity table because if it is then what happens is that when we delete that entity with the ID of one then we essentially deleted both of these entities even though there are logically different entities so that's something that's very important to notice and keep in mind when we're mapping into the database what we're going to do today is we're going to take a complex object and we'll see how we can map it to the database or to the database tables this isn't going to be trivial and we're going to do it in a few different ways so you're familiar with the different options and this is something that if it doesn't make sense to you yet then hopefully as the video progresses then it will make more sense okay so up until now in the series then this is what we have right so this is what our application looks like and what we're going to do today is specifically in the domain layer so we have the menu aggregate that looks like this in a minute we'll look at the Json representation of it but just reminding you that what we have is we have the menu aggregate which internally has many many menu sections and each menu section has many menu items right now each one of the menu the menu section and the menu item all of them are entities so each one of them has an ID and the menu is the aggregate root of the entire menu aggregate okay now the IDS of the entities are all value objects as you can see over here so each one of them is an object which inside has the property of the actual ID value and that's because we want to be able to change this value from a GUI to a string to something else without having to affect anyone else who has a reference to this menu ID great so we have this then the application layer we have the menu repository that is how the application layer can actually manipulate the list of menus so for example when we create a new menu then we use the add method in the eye menu repository interface and the implementation sits over here inside the infrastructure layer so we have the menu repository and the menu is sitting over here in memory and what we're going to do in this video is instead of using the in-memory database then we're going to use a SQL Server now you can notice that we have here a few new files so we're going to have the DB context for our application which will contain the DB set of menus and then the menu repository is going to work against this SQL server using the Uber dinner DB context instead of working against that list of menus that's the only change in our application in the infrastructure layer and that's what we want with persistence ignorance that the infrastructure layer is the only layer that cares about which database we decide to use so ideally what we have is we have over here some interface or if you decide to use the DB context in your application layer then the application layer won't change when you switch either the implementation of this interface or the database provider that's used by the DB context okay not saying that it happens a lot but if it does happen or logically you want to have this separation also for testing purposes great so let's talk about the menu aggregate so this is the menu algorithm and like we said so each menu has a list of sections and each section has a list of items other than that we have here the average rating value object like we said there's no ID in this value object and other than that we have here two lists one list of dinner ideas which is all the dinner IDs that are serving this menu and we have also a list of menu review IDs which is the IDS of all dot reviews for this specific menu great so now the question is how do we take this menu aggregate which is in the Json representation here on the left or you can imagine it in memory sitting as reference of objects and how do we take that and remap it to the corresponding SQL tables okay so to do this that's where the configuration file comes into place so what we looked at before then over here we have the actual con menu configuration so that's the way we're going to do this conversion okay so each one of the Aggregates will have one of these configuration items okay now there are three steps for mapping an aggregate to a relational database so the first step is identifying all the various tables so the first table we're going to look at is the menus table this corresponds to the menu Aggregate and this will contain all the properties that aren't complex objects or that are a value object so looking at the average rating then we can see we flattened it over here and it sits simply as two columns inside the menus table and this is using a feature that's called table splitting or table sharing where you have two objects sharing the same table okay so we have the ID name description and average rating over here up until here then we have the host ID which like we said it's a value object that stores inside the actual value so we're flattening that and we're storing the actual value over here like you can see and lastly we have these two day times that are stored over here and all the rest is mapped to different tables so let's move on to the other tables then we have the sections so the sections also against the table and we'll collect many sections then we have inside here that ID which is mapped to the menu section ID then we have the name of the description where each one of them gets a column as well great moving on to the list of items so the list of items is very similar to what we had before so we have over here the menu item ID and we have here the name of the description that corresponds to what we have over here then last but not least then we have the dinner IDs and many review IDs where each one of them also gets a table now if you're familiar with relational databases then you know this first step is not enough for us to actually map it to a database because we need to define the relationship between the various tables we want anti-frame record or the relational database in general to be able to reconstruct the original menu aggregate based on the data in these tables okay so moving on to step number two then in Step number two what you want is to add all the various foreign keys that are used to define this relationship so the menu section needs the menu ID the menu item needs both the menu section ID and menu ID foreign keys and each one of the menu dinner IDs needs the menu ID as well okay so after each one of the tables added the corresponding columns to identify the parent entity using the foreign Keys then we could move on to step number three step number three is adding all the various SQL constraints this is where we want to make sure that we're defining our primary Keys correctly so we don't have that situation that we talked about before in the earlier slides so let's take a look at it now and understand why it's configured the way it is so starting with the menu section then we said that the section entity is unique within the menu aggregate okay this means that we can have multiple menu Aggregates that all have a section entity with the ID of one and then when we delete this entity using its primary key then we're deleting it across multiple Aggregates that's why we want the primary key of the menu section to not only be the menu section ID but a combination of both the menu section ID and the menu ID this is known as a composite key so we have the menu section ID and the menu ID and both of them together are the primary key so generally speaking there are two ways that you want to take or look at we'll look at owned entities in a few minutes but there are two ways to take own entities and Define the relationship between them and the parent so one of them is to Define that the primary key is a composite key of some something like we're doing over here and the other option is to Define some key that is a shadow property used for the ID which will be Auto incremented by the database and that way we know that the ID indeed is unique because again we want to make sure that the ID of each one of the entities in the database is unique following the constraints so either unique within the entire system or unique within the aggregate okay now if you're looking at this and you're thinking to yourself what's the big deal because each one of these IDs as you can see is a good so we don't need to worry about any clashes between them then I want you to remember that we want to keep persistence ignorance when we're modeling our domain layer so we want to be able to switch the IDS from being guise to Simply for example incrementing inside the aggregate so each one of the menu section is incrementing let's say from 1 to 100 inside the Aggregate and we don't want our database to suddenly break without us knowing great moving on to the menu items then the menu item is also an entity so each one of these is an entity inside the section entity which is unique within the menu aggregate which is unique within the entire system that's why what I configured over here is that both the menu section ID and the menu ID together with the menu item id they compose the primary key of each one of the items that's the only way that we can guarantee that it's Unique within the entire system okay moving on to the dinner ideas and menu review IDs so this is an interesting case so what we have over here is a list of value objects now I'm asking you what's the issue with having a list of value objects and why do we need to introduce here a new column with the ID of the values okay so the answer is that because it's a value object and it doesn't have a key even though logically we'll probably have only one thinner ID value per menu so the same dinner ID shouldn't appear twice inside the dinner IDs list we don't want to count on that and we want to introduce some circuit key that will be incremented per each one of the dinner ideas when we store it in the database and that way we can guarantee that we are recreating the same object that we stored in the database so that's what we're doing with the dinner IDs and the same thing also with the many review IDs so I hope that after these steps then you have a clear understanding about to take any Aggregates that you have and stored in the database now one thing that you want to remember when you're configuring these relationships is that when you delete the menu then you want all the corresponding or inner entities to be deleted as well that's why we're going to introduce the concept of own entities when we move on and we'll see how the sections for this menu are owned by the menu and the items for the menu section is owned by this menu section and then when it's deleted then it's cascaded and deleted as well so we'll look into that when we start configuring this mapping we're going to write this entire mapping so we will have the database look exactly like this soon as the video progresses so when we get to that then I think it'll make more sense okay so let's move on and start coding at last okay so I have our project open in Visual Studio code and it corresponds to the diagram that we looked at before the first thing I want to do is to go to the infrastructure project and inside the persistence folder to create here our DB context so let's go having called Uber dinner DB context and let's say public class Uber dinner DB context and this is going to be the the context for our application but because we don't have the entity firmware core nuket package yet then let's go ahead and add it to the infrastructure project so let's say net add and we want to add to the infrastructure project the package Microsoft dot Entity Framework core and let's just fix the typo so dot net let's run this this will add the nuget package to our application okay now we can say over here DB context and include that corresponding namespace let's close the terminal so we have a bit more room and the first thing we want to implement over here is the Constructor that we'll call the base Constructor with the DB context options so for that let's say public then let's put the name of the class and let's say DB context options for our specific class and let's call it options and let's pass it to the base as following great now let's create our DB set of menus so let's say over here public DB set of menu and let's call it menus yes and let's give it a getter and a Setter and because we're not initializing it from The Constructor and we have nullable reference types then let's shush the compiler and tell it that don't worry this ain't gonna be null great so we have our DB context the next thing we want to do is to add it to our dependency injection ioc container so we can use it in the menu repository so let's go to our dependency injection of the infrastructure layer and where we have the add persistence method let's go ahead and say Services dot add DB context this also comes from the nougat package that we just added and let's say Uber dinner maybe context and over here we have the options that we can use to configure which database provider we want to use now we're going to use the SQL Server database provider so we want to say use SQL server and this comes from another nougat package that's because each one of the providers has its own nougat package so you don't need to add symbols and things that you don't need so let's say net add to the infrastructure project the package yes this thing dot SQL server and this will add the corresponding database which inside has the method that we need to add this provider and the next thing that we want is to put here the connection string but we'll do that later all right so I'll just add the namespace and now that we have this then we can go to our menu repository and use the DB context instead but before we do that I want to make this a bit cleaner so let's create here a new folder and let's call it repositories and let's put both of these repositories inside this repository let's just fix the namespace and then continue okay great so we have this let's get rid of the in memory static list of menus and instead say Uber dinner DB context and let's say deep context yes this thing let's just say private read-only and let's inject it via DOT Constructor and then over here we can use it instead so we can say DB context and then add the menu and then of course we want to save the changes so it's actually persisted in the database so it's called save changes now of course this is still synchronous this is just a step in the way don't worry so this won't stay like this for long but as part of this video this is what we're going to be doing in the repository okay so we have this again we're only updating the infrastructure layer the underlying database changed but the application and domain layer don't care and that's one of the core principles in clean architecture that we have the layers and then we can have the abstractions sit in the inner layers and we also are keeping persistence ignorance that we talked about before great so we have this the next thing we want to do is Define the mapping between the menu and the actual database tables so for that let's open create your a new folder let's call it configurations and inside here let's create the menu configurations which will have the configurations of the menu ever so let's say public class menu configurations and this is going to implement the I entity type configuration for the menu and let's implement this interface and receive the entity type builder for the menu type which we can now use to define the conversion for the menu type now I want us to look again at the table so this this is what we're going to configure now and this is our menu entity the first thing that we want to do is Define the menus table so let's start with that and to make this a bit cleaner let's create here a method and let's call it configure menus table and let's pass it the Builder and let's create this method below then over here we can start defining what it's going to look like so we can say to table and call it menus now this is the default but I just want to be explicit as part of this video just so you see more methods and if you decide that you want to be explicit in your application then you know how to do it okay so we have the table name which is menus now let's start defining this so let's open the menu on the right and let's close the file explorer so you can see something so opening the menu aggregate then let's start with the menu ID which sits inside the entity which is referenced by the aggregate root right so I'm reminding you we have the aggregate root which extends The Entity and over here we have the actual ID so let's first configure this ID so let's say over here Builder let's say has key and the key for this table like we said is going to be simply the ID because the address ID is unique within the entire system so we can say m.id and that's it the next thing we want to do is we want to Define how the menu ID is going to be mapped to the database because I'm reminding you if we look at this menu ID then it's a value object that contains inside the actual value which is what we want to store in the database so the way we do that we can say builder then we can say property then we can select the ID property and we can say what the conversion is on the way into the database and what the conversion is on the way out of the database so we can say has conversion and then we can say on the way in then let's take the value and on the way out let's take the value and let's create the menu ID using this that so we can say create and pass it the value like so so this is calling the create method over here over here which simply Returns the menu ID we're still missing in the menu in the domain layer in general many enforcements of invariance this is a topic that we'll cover in a future video What's the difference between domain validation application layer validation and what are invariance we haven't talked about that yet so don't worry we'll talk about all that in an upcoming video so for now this is what we're going to have also another thing is that by default empty framework core tries to generate the a the value for the ID and because we're generating it inside our domain layer then we need to say over here value generated never and that's it for now maybe soon we'll see how we can do it across all of our types and we won't have to write this again and again for each one of the types because all of our types currently generate the ID okay moving on so we configured that this the primary key it's a good because that's the value of the that's the type of the ID and that's it for now let's go ahead and add the name in the description that both have a maximum length of 100 characters so let's say Builder and then I'll say property and the property that we want is the name and over here I'll say has max length and let's say 100 and let's do the same thing for the description as well so let's say description has max length of a hundred okay the next thing we want to do is we want to take the average rating and flatten the average rating value object that we have over here and like we said we're going to do that with owned entities and this is a feature that's called table splitting or table sharing where we have two entities that are sharing the same table like we can see over here so to do that it's very simple all we need to do is the following you can say Builder and then we can say owns one and select the property that we want in our case it's the average rating and then by default these are the names of the columns that we'll get but we can customize it if we want so we can say over here that we have the average rating Builder and we can Define over here that for the property of the let's say value then it has a column name of whatever we want and update it but let's leave it for now because we're going to do this something similar soon for many of the other tables okay so we have this the next thing we want is to Define that the host ID so again this is over here a value object that contains actual value we want to flatten this as well and store it over here so let's say Builder and let's say that for the property of the host ID then there's a conversion on the way in conversion on the way out so let's simply copy what we have over here and paste it below and this will be the host ID great let's just include the correct namespace and that's it for now the last thing we have over here are the two day times that will leave as default for now great so we have our first table configured so this is the menus table now let's move on to the menu section which corresponds to this thing over here so let's go ahead and create here a new method and let's call it configure menu section table and let's create this method and what we're going to have over here is the definition for how we're going to store this property this sections property and I'm reminding you that the actual data sits in the underlying field of the section so that's the backing field for this public sections and this is a list of many sections and each one of the many sections looks like this it has a name a description and then the same story with the items that is stored as a backing field okay so we have that let's go ahead and configure the menu sections table so the first thing we want to do is similar let's say Builder and then owns this time many and what we own many of is the section and over here we have the section Builder that now we can use so let's say two table so in our case the name of the table that we decided is menu sections so let's go over here and say menu sections and the next thing we want to do is we want to define the foreign key that is the menu ID right so I'm reminding you we have the menu ID which is a foreign key and it's coming from the parent entity which is the menu so let's say over here section Builder and then we want to say that it has an owner and that has a foreign key that is called menu ID great so we have that let's move on to the menu section ID the name and the description so we have the menu section ID let's say property then we have the menu the ID and we want to give it the name of menu section ID we also want to say that the value for this is never generated and also we want to define the conversion so on the way in we want to take the value and on the way out we want to regenerate this menu section ID and let's call create I see I forgot to create the create method so I'll create it and then I'll return to this part okay so I created this method and now this works as we expect okay moving on we have the name and the description so let's say property and let's say name and let's say has max length of 100 and same thing for the description so let's say description great so let's see we're not missing anything so we have the name and the description which both have a maximum length of 100 we have the primary key that is composed of both the menu section ID and the menu ID and this is something that we haven't configured yet so let's go back over here and let's define what the key is for this table so ideally what we would do is we would say section and then over here we would specify the properties that compose the ID so I would say ID and then over here we would select also the menu ID but because it's a shadow property and it's not accessible via the section object then what we want to do is say over here the names of the columns that compose the key so in our case it's the ID and the menu section ID like so and that's it right so we have the menu sections table configured let's move on to the menu items so the menu items sits inside each one of the sections so similar to what we did with the menu in the menu section then we'll do the same thing with the menu section and the menu items so over here we have the Builder for the menu section entity now we can say over here that this owns many and what it owns many of is the items and then we have here the item Builder that we can use to configure each one we can configure the menu section items table so first of all let's say that the table name is menu items like we have over here so let's say menu items next we need to say that it has an owner so with owner and then we want to say that it has a foreign key and now over here it doesn't have only one foreign key but it has two foreign Keys both the menu section ID and the menu ID so for that what we want to do is we want to say to specify both of them so let's say that it has the menu section ID and the menu ID great we defined this now we can go ahead and Define the other things in our table so currently we did only this and we haven't defined that they're part of the primary key yet so let's go ahead and Define the ID so it's similar to what we already did in the past so let's say over here that we have the item Builder and we want to say that for the property of the ID then first of all value generated never we also want to say that it has the conversion on the way into the database and on the way out of the database so on the way in we're taking the value and on the way out then we're reconstructing the menu item ID so let's say menu item ID and let's say create and also here I forgot to create the create method so let me just create it and come back great so now we have the create method so we can pass it the value and reconstruct our ID we also want to give this column a name so let's say has column name and the name that we gave it gave it was menu item ID so let's call it over here menu item ID and that's it we also have the name the name in the description and then we want to define the primary key so let's go ahead and let's copy what we have over here because it's going to be similar so let's paste it here let's fix The annotation changes to I and same thing over here great now that we have that the next thing we want to do is we want to define the primary key so like we said this time the primary key is composed of both of these foreign keys and also the menu item ID so to do that let's say the following let's say that it has a key and the key again because we're using Shadow properties in the definition of our composite key then we need to define the column names so the column names is the two columns that we defined over here and also we have the ID so before we put the explicit name but of course we can also do name of and over here we can say that we want the menu item and the ID property which will give us the string ID and then if we change the name of this property in the future then we didn't break the database as well so it's just a better practice to use name of and not what we did up here great so it looks good but even though we configured everything and it corresponds to what we have over here there's one more thing that we need to do because I'm reminding you again that the actual items if we go to the menu algorithm then the actual items that sit behind the sections property is the private list of sections and same goes for the menu section with items so we need to go ahead and Define that that's the field that we want to enter thermal core to populate that underlying field and not the I read only list that it can't right so let's go ahead and do that so let's say over here Builder and then we want to say in the metadata find navigation the navigation property that we want to find is the sections so again we say name of menu Dot sections and then we want to say that the access mode so the set property access mode would say property access mode field and this will give us what we want this method returns null if it can't find the section but we know this section will always be there so we can tell the compiler to chill okay so we did this we need to do the same thing for the items now I don't know why the find navigation isn't available on the nested builder then this is what I default to in nested Builders so let's say section Builder and now let's find the navigation that we want to configure in our case it's the items right so items and then over here we want to access the metadata and to Define that the backing field for this property is the items so let's say set field and let's say items and let's now Define that when we're accessing the items then we want to use the access mode of the field like we did before great so we have this now it's fully configured and we can move on to the last two tables which are the menu dinner ideas and the menu review IDs which are both a list of value objects so let's go back over here to our main configure method and let's define here the configure menu dinner ideas and same goals instead of dinner let's say review IDs and let's create the corresponding method so starting with the dinner ideas then we have the Builder where we know the drill so we want to say that it owns many of the dinner IDs and now we have the Builder for the dinners dinner ID table and we can say the following so two table and this we want it to be menu then IDs we then want to say with owner and then has foreign key and the foreign key like we said is the menu ID corresponding to this thing over here great so moving on we want to configure now the primary key for the table so we can say over here has key and like we said we do want this to be an auto incrementing I in that will be generated by the database so for that let's say has key and let's simply say over here ID this will generate a shadow property for each one of the dinner ID objects so I'm reminding you that the dinner ID object looks like this so it has inside the value which is what we're actually storing inside the database so we'll create a shadow property called ID and it will populate it with an incrementing value when it's stored in the database so let's go ahead and configure this property so the value property we want to store under the name dinner ID so let's go over here and let's say that for the property of the value then few things first of all has column name of dinner ID let's also say value generated never and I think that's it right so this is what we're looking at so we have the dinner ID the menu ID and the ID so looking over here we have the menu ID the ID and the dinner ID great the last thing that we're missing is again to configure that we want to populate the underlying list so going back to the menu then we can see over here that's for both of them it's the same drill with the read only list and behind the scenes we have the actual underlying list of dinners and reviews so for that we already know the drill so we can say Builder and then we can say metadata find navigation then we can select we can need to give it the name of the property that the navigation property so in our case it's menu dot dinner IDs and then we want to say that when it's accessing this property then we want it to populate the underlying field so for that let's say property access mode and let's say field and let's just give it an exclamation mark so the compiler can relax and let's do the same thing but instead let's call this method configure menu IDs and then let's basically take every place where it says dinner and replace it with review right let's say dinner and let's change it with review all right so we have that let's see if it compiles so no we have the dinner review IDs and let's see if this works I'll say review menu review ideas sorry yes let's see if this works and over here also we want to change this to menu review IDs and that should be it so we have all of our five tables configured again this method over here configures two tables also the menu sections and also the menu items now that we have all of this defined then let's first build our application and see that it builds as we expect so running.net build guys I can already see that we have an error and I'm missing a semicolon over here so fixing this and let's re-run the build great okay so we have our menu configuration the next thing that we want to do is we want to generate the actual migration so going back to our slides then let's talk a bit about the technical details so we added two packages up until now the empty framework core and the anti-firmware core dot SQL Server which is our database provider and the next thing we're going to do is we're going to add the net tool of.net EF and this allows us to create migrations of these database and so on but for creating the migration definition then we need another nougat package which is the design nougat package so let's go over here and let's say net tool and then we want to list all tools that we have globally and see if we have net EF installed so I already have it installed but if you want to install it then all you need to do is say.net tool install and then to install it globally that will install it but because I already have it then I don't need to do it now what we want to do is say.net EF and they want to create a new migration like it's suggesting over here but let's write it out so it's easier to comprehend so we want to call the migration initial create the project that contains our DB context is the infrastructure project right infrastructure project then we would need to Define what the startup project is so we can say start a project yes and we want this to be the API project now you can write it also in a more concise way so over here you can simply say Dash p and over here you can say dash s so that's what we're going to do from now on just because some of you are watching this on a phone and the text is giant so just to make things a bit more concise okay so we did this let's click enter and see why it might not work okay so it's failed and that's because of what we just said so we need that API project to reference the design nougat package so for that let's say dotnet ad and we want to add to the API project in the package microsoft.entifiercore.design so running this this will add it and then we'll run the creating migration again so let's say now the same thing let's run this and see if it was able to generate the migration successfully if not then we'll try to understand why okay so it didn't work and we can see that we have no suitable Constructor found for the dinner ID now it's giving the dinner ID but in fact if we look at our domain layer then let's look at the menu for example let me close all this so looking at the menu then this probably bugged you if you're familiar with anti-famil core already so we can see that we only have public Getters for all of our methods and also we have this Constructor which in our case it's private but even if it wasn't private then all we have is this Constructor that requires many arguments and when anti-framel core tries to create an instance of the menu then it uses reflection and it can do it with this Constructor it needs a a default Constructor so for that let's go ahead and create a default Constructor in all of the objects in our domain layer that have to do with the menu so that includes the menu the aggregate root The Entity and all the various entities the other entities that we're using and also let's change all the properties instead of being only get to be get and then private set all right like this so let me do that for all of the relevant entities and then let's continue okay so I'm back and what I did off camera is the following so looking at our domain layer and then at the menu aggregate then I changed all the various properties to also have private Setters like we can see over here and I also added a private parameter-less Constructor for the menu but not only for the menu also for the aggregate root and for the entity as well so as you can see over here and I did the same also for all the various relevant entities so for the menu item for the menu section basically any object that any firmware core will try to instantiate using reflection so it needs a private parameter list Constructor and same thing with the private Setters so now that we have that out of the way then there's one thing that I didn't forget to do and that is in the Uber dinner DB context so what we want to do is we want to tell antifremacore to scan the assembly find all those I entity type configurations and add those configurations so to do that we want to overwrite the on model creating method in the DB context so let's say over here all right and then we have the on model creating method and over here we can say model builder and then apply configurations from assembly and over here give it the currently executing the send Loop assembly or the Uber dinner DB context whatever you prefer so I'm just going to say type of uber dinner DB context and select the assembly but you can do whatever you want that's the assembly marker or however it's implemented in your service also I wanted to say that this is the place if you want to specify that for all the entities in your DB context then you don't want to generate the ID for the primary key then this is where you you can do it you have the model builder you can iterate over all the entities and and say that you don't want to generate the value for the ID I'll link the code over here the Snippets of code that you need to put for you if that's what you want to do okay so we have this out of the way let's try creating a migration again and saying if it works so let's say over here dot net and different core migrations add the name of the migration and then the details let's see if it works okay so to my surprise it failed this is the first time that I didn't expect it to fail so let's debug and understand what the issue is so looking for the actual error then we can see the following you can see the property menu section ID cannot be added to the type menu section because no property type is better okay so it says that there's no menu section ID type so let's go to the menu configurations and let's look for the menu section ID which is inside the menu section so let me close the terminal so we have a bit more room so we're in the menu section and we have the menu section ID and now I can see the mistakes so I'm looking back at our diagram so let me open it over here then we can see the following so we want the primary key to be composed of both the menu section ID and the menu ID right but what we gave it here is the menu section ID and this doesn't exist right so this ID is talking about the ID property and the menu section ID needs to be changed to menu ID right so let's change this to menu ID let's try building or creating the migration again and seeing if it works okay so the migration was created successfully and we can all sign relief and make sure that this looks like we expect so I'm looking at the migration definition and we can see we have here some errors and this is caused by rules that we have in our editor config so we can go to the editor config and tell the compiler to ignore them by saying that everything in the migrations folder just is considered as generated code so we can say generate generated code equals true and this should get rid of all the various warnings great okay so we have the menus table and we already remember this I think by now so we have the ID we have the name in the description then we have the average rating value object then we have the host ID and the created daytime so let's see what it looks like we expect so here the maximum length is 100 like expect and it's the correct type then we have your double and an INT great that's what we expect for the host ID then we are storing it as a string so I forgot about this but that's that exact idea of this solution that we don't care about the underlying value So currently it's a string but it can change in the future so something we might want to do is we might want to cap this to only be some length if we prefer okay so we have the primary key which is the ID that's the ID of the aggregate moving on to the menu dinner ideas so this is the table of the value objects so this is where we generated an ID so this ID that we're looking at corresponds to this over here so we have this ID which is Auto incremented and created by the database and we have here the dinner ID and the menu ID which are simply properties the primary key is this ID dot init and then we also see that what we talked about so all the various tables are configured to be deleted if their parent entity is deleted right moving on to the menu review ID so we expect this to be exactly the same because we did copy paste so yes same thing over here we have the ID and everything here is as we expect moving on to the menu sections so we have the menu sections and if you remember for the menu section so we have the primary key also the menu ID and also the menu section ID so that's what we expect to see over here and indeed these are the primary keys and we can also see that the values are as we expect so we have the menu section ID the menu ID and we have the name of it in the description that are both 100 characters long next we have the menu items the menu items has two foreign Keys which is the menu section ID and the menu ID and the ID of the actual menu item entity which is this but the primary key is a combination of all three of these so that's what we expect to see over here and in fact that's what we have so the primary key is composed of all three properties next we can see that the name of the description are both only 100 characters like we defined and that's it right so everything here is also like we expect great so there's this isn't very interesting so we won't go into this but it looks like we expect so now let's go ahead and create our SQL server in a Docker container and then connect to it create the database apply our migration and see that everything works end to end like we expect so going back to the slides then we created the migration the next thing we want to do is to update the database so for that what we want to do is we want to create a dark container so even though this is in the illustration like this in actuality it's going to be inside a Docker container because I'm working on Mac OS and the SQL Server doesn't run on it natively like it does on windows so that's what we're going to do and also so all the various platforms are happy also Linux Microsoft and Mac OS and I was curious to see how the icons will look with eyes so I left it so if you were curious then now you know Grace so the first thing we want to do is we want to have Docker running on our machine the next thing we want to do is to make sure that we don't have the SQL Server already running so looking at that we can see that there's nothing running currently and also I made sure to get rid of everything so if we look at the images that we have then I don't have any images either great the next thing that we want to do is we want to pull the SQL Server image so we can use it to create the container so for that let's say Docker pull and this will pull the image great so now if we look at our images then we can see that we have it installed the next thing we want to do is to actually create the container so let's say Docker run and this is what we want let's walk through it so we're saying that we want to accept the end user license agreement also we're saying that for the system administrator then the password is my bank password which is amico123 and then an exclamation mark and the port that we want to run on we want to run in detached mode and this is the image that we want to run so running this should create the docker container so let's say Docker container list and let's see what we have so we can see that now it's running from six seconds ago we can also Docker PS it's a shorter way to do it and now that we have that let's go ahead and try to apply our migration so for that we're going to use the anti-frame recorder.net CLI tool again so let's say net EF and then database and we want to say update and we want to Define that the project that contains the migrations is the infrastructure project the startup project is that API project and then we want to define the connection string which is the following so the server is a local host the database is Brewer dinner that's the database that we want to create then we have the user ID which is system administrator or system administrator then we have the password which is the password for my LastPass account which is amico123 and exclamation mark we need to escape it so let's Escape it and the last thing we want to say is encrypt false this is because we're running locally of course that you don't necessarily want to write this when you're running this in production environments okay so running this this will apply the migration to the database so let's give it a moment and see if it works okay so I finished running and it looks like it ran successfully let's go ahead and make sure for that we're going to use a visual studio code extension that's called SQL Server so let's open the extensions and over here search for SQL server and we'll find the first one which has many downloads and somewhat love from the community so let's go ahead and open this extension and over here we can see the databases that I already have let's remove them so we don't have any spoilers of what's going to happen and let's add our database so for that we want to put the connection string over here so send drill servers localhost the database we want to connect to is the Uber dinner database then we have the user ID which is the system administrator and the password which is the password for all my essential documents which is amico123 and exclamation mark let's press enter and this will connect to this database so great the profile was created and connected and now we can see that it indeed created The Uber dinner database and inside here we have all the various tables and we can make sure that it looks like we expect so we have the menus for example and here we have all the columns and we can make sure that this looks like what we defined before in the tables great so now that we have this let's try running the application end to end and seeing if it works so let's say dot run and the project that we want to run is the API project let's go to our requests folder and over here let's use the create menu request to actually create the menu but let's first Let me refresh the token and put here some valid details okay so the server is running and I created a new token and I put here some details this is just the details that we defined in the API definition so I simply took it from there and I paid pasted it over here let's run the request and see if it's stored in the database successfully so running this let's give it a moment and we should see also the output over here and we see that it failed okay now if you're wondering why it failed then I want you to take a second and think why it might have failed and the answer is that in the dependency injection of the infrastructure layer then we said that we're using the SQL Server database provider but we haven't specified the connection string which it'll use to actually connect to the database so that's what we want to do over here so you know the drill I'll write it okay so I wrote it out and same thing like before so the server is localhost the database is uberdinner that user ID is a system administrator the password is the password that I use for everything important in my life which is amico123 exclamation mark and the last thing you can either write encrypt false or trust a certificate true if you're working locally like I'm doing your following my steps so both of them will work and let's try running the application again and same if it it works now okay so I'm running the request and let's see if it runs successfully and we can see a few things first of all we get the response which looks good and also we can see over here that we have the details of the request printed to the console so let's query the menus table let's select the top 1000 and again this is part of the extension that we added before so this will create the query run it for us and here we can see the details like we expect with everything that corresponds to the response we just got great so we have that the next thing we can look at is perhaps the menu sections so let's say select top 1000 again this will create the query for us query the database and here we can see all the various values and for the menu items then we expect to have here the two foreign Keys both the menu ID and the menu section ID so we say we have here the menu item id the menu section ID and then the menu ID and this should correspond to the menu ID and the menu section ID so if I open the actual response that we got on the left and let's keep this on the right then we want to make sure that the menu ID really is the menu ID like we can see over here the menu section ID is indeed the menu section ID and the item ID is indeed the item ID right we can see that it's configured like we expect it's important to just make sure that the database was created like we expect it looks like we expect and that's true every time we do a migration you just want to make sure that you're doing things correctly so I think that's it for this video I hope you enjoyed it and you learned something new you have no idea how much time I spent preparing for this video and creating this video so if you enjoyed it make sure to smash the like button leave a good comment smash the Subscribe button and I'll see you in the next one
Info
Channel: Amichai Mantinband
Views: 59,698
Rating: undefined out of 5
Keywords:
Id: 5_un3PUER8U
Channel Id: undefined
Length: 60min 0sec (3600 seconds)
Published: Mon Jan 09 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.