Visual Studio Toolbox Live - Clean Architecture for ASP.NET Core Apps

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] hello everyone sorry for the late start there welcome to another live episode of visual studio toolbox i'm your host leslie richardson and today i'm joined by steve smith who is a microsoft mvp mentor trainer about how to have better software at the end of the day and today are going to be talking about how to leverage clean architecture and asp.net core apps so welcome steve thanks glad to be here wait yeah thanks for thanks for being here so would you like to share a little bit more about yourself and what you do on average yeah i i am one of the founders of nimble pros which is a consulting company we help teams write better software faster i do a lot of work with architecture very fond of domain driven design and fortunately domain driven design ties really nicely in with clean architecture uh which also ties in really nicely with building things like microservices and stuff which of course are very popular today so um that's part of what has drawn me into these topics uh a few years ago i put together a clean architecture solution template on github and so i thought maybe today we could spend a few minutes talking about clean architecture in general and like what is it uh why would you want to use it versus other other ways of structuring your your stuff and then look at the specific template and how you could use that for your projects absolutely yeah so do we just want to dive into why uh is clean architecture so important and why should people use it yeah that sounds good uh let me just throw my slides up there we go all right so uh let me get through a couple quick uh things where people can find me or find additional resources if they need to uh where's my slide controls there we go so there's a pluralsight course on domain driven design that talks a lot about the principles that kind of relate to this if folks have pluralsight access they can check that out there is a free microsoft ebook that i wrote that's available with the eshop on web sample which is a like a full featured application that uses clean architecture you've got my information here if you want to talk to me and beyond that uh got a newsletter and some other stuff all right so i want to go through this a little quickly as far as the slides because i want to bore people and we're already a little bit behind time but there are a couple of principles that really guide this architecture so the first one is the idea of separation of concerns where you don't want to have low level concerns like data access or how you talk to the cloud or things like that coupled with your high level concerns of like what is the business abstraction we're trying to do i'm trying to process an order or you know create a new insurance claim that doesn't need to be concerned with which type of data store i'm using or something like that so separation of concerns is a big one the big three that you want to keep separate are generally data access business logic and then your ui and so we'll see how that's kind of like one of the larger breakdowns that you get in clean architecture now that's not unique to clean architecture there's other architectures out there that are very popular which we'll talk to about in just a minute they use the same breakdown but they don't have the same benefits as clean architecture and we'll see why in just a minute another principle is single responsibility you don't want your classes to be doing too many things kind of relates to separation of concerns um so you want to make sure they only have one reason to change uh usually along the spectrum of different classes of different kinds of things that class might be doing like validation or persistence or business rules or things of that nature all right you want to try not to have too much repetition in your code getting rid of repetition sometimes introduces coupling so you can't just get rid of all repetition but if you see the same kind of cross-cutting concern and you have to write code for it again and again and again there's usually a better way to structure that with some patterns uh things like the uh decorator design pattern come into play really a lot of the time for that type of thing so we'll skip some of that and some of that and we'll talk about dependency inversion so single responsibility and dependency inversion are are kind of the two ends of the solid principles where the d is the the d in solid uh is dependency inversion and with dependency inversion the idea is that you don't depend on low level details and so if we look at the definition for this it's that both your high level classes and your implementation detail level classes should depend on abstractions which in c sharp generally is going to be interfaces and so this ties into a clean architecture quite a bit as we'll see because it definitely leverages the dependency inversion principle quite a bit another related principle is the explicit dependencies principle which says that your classes your services especially should request all of their dependencies in through their constructor that means they should be explicit they should like list them and say here are the things i need if you want me to do my job correctly um so what this does is it eliminates hidden dependencies so that when you're creating a class you don't start using it and then find out at runtime that oh it needed this connection to this database or this configuration value or something like that if it needs those things it should ask for them up front i liken this to if you're if you're cooking and you've got a recipe and you read the ingredients for the recipe to see if you've got everything you need you check the refrigerator or whatever and then you're like great i'm going to make this you start going through the recipe and then halfway through the recipe it's like oh and you also need this you know a pound of something that it didn't tell you about you're like i don't have that like why are you telling me that now it's like i'm already halfway through mixing that right here already halfway through already rested all the ingredients you got like that's that's frustrating don't frustrate people that are trying to use your classes tell them everything that it needs at the top all right um so because of the dependency inversion principle because we don't want to depend on the implementation details we want to depend on the abstractions and the interfaces we want to put those abstractions somewhere that they can be accessed by the low-level details by the high-level business services that are going to use them and also by the user interface entry point that spins up the application that's where we're going to do dependency injection and we're going to call configure services in our.net core app so it needs they need to be accessed by all these things so that kind of tells us in our solution structure where do we have to put the interfaces so that all these things can get to them and we'll see that in just a second all right so then why do we need these interfaces what's what's the reasoning behind it um the main reason is so that we can break apart the the two pathways that our code uh tends to run through the the first one is at compile time right in a strongly typed language at compile time the compiler is going to verify that class a can reference class b can reference class c when your code is doing something like in class a it news up an instance of class b and calls a method and in that method it news up an instance of class c right so when you run this code the control flow is what you see on the right and it is a direct mirror of the reference structure that's what all your code always looks like if you don't use any interfaces or abstractions as soon as you start introducing interfaces though now we can make it so that at compile time it looks very different from at runtime and at compile time we depend only on an interface and at runtime we can decide which implementation we want to use and that has a lot of benefits when it comes to writing modular software writing testable software being able to upgrade software in a safe manner because you can just replace a module instead of having to change it all these things have a lot of benefits for the quality and maintainability of our code so these are the kind of the setting the stage for why you want clean architecture then for architecture in general right the goal is to make the right thing easy and the wrong thing hard so the reason why you're bothering with having some kind of structure that you're going to use for your application is to make it so like by default the easiest thing you could do in that structure is going to be the right thing and not that you've got this you know thing where you have to remember to do things a certain way or else everything falls apart um you know you want to just be like obvious that you do the right thing force your developers into the so-called pit of success okay so piece of advice that's great words to live by yeah i i i didn't make it up i heard it from various places but i think they i agree they're great things you want to strive for because i just think of like especially when you are learning up on an existing code base or have to expand upon an existing one and these practices aren't being followed it's so difficult to figure out how to squeeze in your contribution or even understand what you're doing sometimes exactly exactly all right so things we want to do uh is is avoid directly depending on the infrastructure from ui now why because we're following the dependency inversion principle we don't want to have uh some controller class that's directly opening a file or talking to a service when we want to have that go through an abstraction through an interface and so we shouldn't have it call into the infrastructure class uh things infrastructure being stuff that talks to things outside of your application right whatever that might be but if it's outside of your application's process it's an infrastructure concern and it should be pushed over there we also want to make sure that the uh business domain classes also don't directly depend on those and so that's going to in inform how we design our solution uh we want to take care that certain patterns and cross-cutting concerns can be dealt with in a in a simple way that that affects the whole app at once without us having to copy paste the code over and over again so that's another goal and so with those goals in mind let's talk about a pattern that has been around for many years which is sort of this classic interior architecture and this is a pattern that i taught and and was one of my first conference speaker engagements like my session was on this in 2001 and one of the slides i had was this one which was from msdn uh the old microsoft documentation site for developers and and this is a great example of what you know end tier architecture looks like where you have your presentation or user interface layer and it talks to you know maybe a business layer of some sort and then a data access layer and then ultimately it talks to the data source and so you know to simplify that it looks something like this and the arrows represent dependencies so your data access layer is written for a specific data technology right so you've got a data access layer that's talking to sql server which is different from one that might talk to oracle or what have you um and so it's got a dependency on the database including its structure and other things right and then the business layer depends on the data access layer which means it too depends on the database because these dependencies they're all transitive and that of course means your ui layer depends on a database so at the end of the day with apps that are built this way of which there are many and i've built a few in my career everything ends up depending on the database now this can be a problem if you're trying to build things using unit tests uh and continuous integration servers that can just spin up and run all your unit tests and not have a database sitting there ready to go um there are solutions where you can use a test database or sometimes an in-memory database or other things but it's a lot easier if you don't need that stuff and you can just unit test things in in my experience okay so a large burden on the database what's that it's quite the large burden on it on the database yes exactly and and for a lot of uh companies that have followed this for a while and they have more than one app right some of them have dozens or hundreds of apps a lot of times they only have one database and everything depends on that same database yeah yeah all right so we can we can flip this we can apply that dependency inversion principle and create a domain-centric design um which which goes by many names but um clean architecture is probably the most recent and common today and the idea is that you've got a domain model so if you're following domain driven design this would be your domain model um and it has you know a model of the the problem space so it has entities and interfaces and services other things that make up your understanding of the the problem space that you're trying to write a solution for uh and it has these interfaces that act as contracts for working with those domain objects now if you're not following ddd just everywhere i say domain object you think business object and it's essentially the same thing so then everything else in the application should depend on these business objects or this domain model so with clean architecture it's also been referred to as onion architecture because of its layers and rings before that it was hexagonal architecture which is a funny one because it just had to do with how someone drew a picture and ports and adapters right and so an onion architecture diagram of clean architecture looks something like this where the middle three rings the domain entities the repository interfaces the service interfaces that represents your your domain model your core of your application um that's sort of like the crown jewels of the app because it has all your business rules in it it has all the things that make your system unique and do what it does around the outside in that purple layer that's where all the other things interact with this business layer and that includes the user interface any infrastructure concerns for data access any things that talk to other services etc all that stuff is pushed to the outside and all the dependencies all the arrows point toward the core is the idea another variant of this is the ports and adapters or hexagonal architecture diagram that looks like this uh and of course you can tell why it's called hexagonal and not pentagonal or octagonal or something is because somebody decided that those middle shapes should be hexagons but it has these purple dots here these are the ports when you think of ports and adapters those ports are interfaces and so the implementations of those interfaces are these these things around the outside like an in-memory object store for persistence versus a relational database store for persistence those are implementations of those interfaces and those are the things that would live in another project in infrastructure and get injected into your application so use one or the other all right so the rules for clean architecture are pretty simple and here's where i would make a fight club joke but too many people haven't seen that movie now so oh no we're at that point now i know it's crazy um so the application core uh is where the domain model lives right that you sometimes i usually call it core because it's like the core of the onion architecture sometimes you'll see it called uh domain uh or some combination thereof i think in eshop on web it's called application core there was a little bit of a problem for a brief while when net core came out and there was some confusion there but i think we're moving past that here in the next year or two hopefully um and so that has your domain model um all the other things point toward that core project or that inner uh project and then it's where the interfaces are defined and the outer projects implement them that just has to be how it works because you have to have a reference to an interface in order to use it um and so they have to be defined inside the innermost most dependent upon uh projects generally all right you want to avoid directly depending on the infrastructure project the infrastructure project has all the nitty-gritty implementation details of how you're going to do stuff and so if any other part of your application has a direct dependency on it because of that transitive dependency you're now directly depending on the file system or cosmos db or your sql server or whatever it might be and you don't want to depend directly depending on those in your whole application it's fine to directly depend on them in the infrastructure project that's what those are for but you want to keep your your ui and your especially your business objects uh independent from those implementation details okay some of the features of clean architecture before we jump in and show the code it's framework independent so it works regardless of which framework you might be using it doesn't care what kind of database you're using or even if you're using a database uh it's ui independent so i'm going to show you an asp.net core solution but you can use it with a windows service or a worker service or an azure function it doesn't really matter it's it's pretty agnostic to all those things it's very testable especially the the core domain model that's one of its best features in my opinion if you want to refactor to it you already have a system there's basically a few ways you could start right if you're starting greenfield then it's best to just start from something that's already properly organized and it'll save you a few hours of trying to figure out how to structure things and that's what we're going to look at in just a second is the the starter solution template that i have if you have an app that is just one project like one asp.net mvc or asp.net core application then that's easier to refactor into these separate projects the hardest place is where you have a multi-layer architecture already and it's not following the di dependency inversion approach it's following that end tier structure that i showed you where the ui depends on the business logic which depends on the data layer that's going to take a lot more time and effort and you may want to get some assistance from someone if you need it to help you with that refactoring all right so now what goes into the core project at the center of the universe in these applications this is where you're going to have all the interfaces that relate to your business model there might be some interfaces that live elsewhere but but most of them are going to live here you're going to have your domain model which is going to be made up of entities value objects aggregates these are ddd terms but they're just different classes that represent parts of your domain model you might have some domain services that perform some logic you may have some domain exceptions or business level exceptions i'm a big fan of custom exceptions for things so that if you know you go to try and add a product to an order uh but for whatever reason the product is null uh instead of you getting a null reference exception you should get a product not found exception or argument knowledge so i don't know it's probably not the best example but you should get a business level exception that says that this thing was not what i expected uh instead of a low-level argument null exception or null reference exception that's the worst of them um that's that's definitely my least favorite exception is the null ref because it never tells you which thing was null like right you have to know like at least tell me the name of the variable or the type of the variable or something it's like um nc plus plus the worst one is the segmentation and where were you when this happened i don't know deal with it it's 2 a.m good luck um another set of things that are often in your domain project are domain events and and oftentimes they're event handlers so if you don't know domain events it's worth spending some time to check them out these provide a great way to decouple things inside your domain model so that when something happens and some other stuff is supposed to happen too you can separate those out so you don't have to have one method on an entity that says like hey when i check out i need to charge their card you know decrement the inventory create a shipping label send them a notification blah blah blah like all those things don't have to happen right there you could just trigger an event that says hey customer checked out event and then all these other things could happen as handlers for that event instead of having to happen all inside one huge method it's a great way to break up some some things in your code and then specifications which we'll look at in just a second specifications are a way to encapsulate query logic which is data access logic really but it's agnostic of your data source and put that inside of your domain model the nice thing about specifications and we'll look at them in just a second is that they have a name so you know you can you can see what it is by referring to it a lot of folks these days when they're building your data access even if they're using uh some type of abstraction layer a lot of times it's it's just link code that they're passing around and so like well give me customers you know c angle arrow you know c dot name equals this and country equals this and company equals that and they have this big blob of link that is the data access code um but that ends up spreading all over the application you'll see link queries and controllers and link queries and services and um they're not in any one place and a lot of times they're duplicated and then maybe one of them needs to change you change it in one place but not the other now you've got behavior that's not consistent um you can't really just save a link query by itself right and give it a name easily uh and so specifications kind of solve that and give you a way to store that query in the link logic that goes with it and then use that specification with entity framework very easily for instance all right so let's look at that in code that's enough of me talking and let's briefly talk about how you get started with this template so i just ran this uh right before we started um and so the relevant code is right here it says dot net new dash i which means install our dallas dot clean architecture dot template that template is out on nuget uh and and so if you do a nuget search for for our dallas clean architecture uh you'll find it uh and then you run this one time and now you have it installed uh and so if we go to another folder like dev scratch and then [Music] scratch if i could type there we go uh and then we run uh a command to create a new solution which i have already over here to copy paste so we'll create a new instance of clean architecture using microsoft.vstoolbox as our company.project name it'll go ahead and create that and it created it successfully so then we'll go into uh a new instance of visual studio oh so i do have a question from chat while we set up from here go for it well learning asks what's the purpose of the application layer in ddd what's the purpose of the application uh it's for application specific services generally so if you have um things your application needs to do that uh you want to combine with um the you know the view models or the api models or the things the application level of abstraction is working with then you don't want to put those things into um sorry i'm trying to talk like well i find this there we go you don't want to put those things into the domain model because now the domain model would know about those user interface concerns and so for me when i'm building things and i use an application layer which i don't always but when i do that's that's what's going in there it's it's higher grain services that are designed to support what the user interface needs to do and then the application service will work directly with the domain mode a lot of the time you can have your your ui work directly with the domain model though um and there's not a huge issue there in my experience all right so here's what we got when when i ran that so you just watched this thing run it created all these files and and you get this solution template not just a project template um but an actual full-fledged solution um that has the the core the infrastructure and web projects as well as three different types of tests that it's going to run that you you can see and there's already tests in here for you okay so looking at core we were talking about the different types of things that live there we were saying like aggregates and entities and things like that well an aggregate is basically an entity that has usually has children and an aggregate you can think of as being a unit of persistence so when i fetch this thing from the database i expect it to have this whole thing not just not just this one thing so if you have like a master detail type of relationship like when you fetch an order you want to include all the order items with it then that would be an example of an aggregate in this case there's a sample that comes with it that you can throw away but it's just there to kind of show you how things work which is this notion of a project uh and the project let me make this a little bigger for you that's good the project has a bunch of tasks a bunch of to do items on it uh and so it just includes this list of to-do items and they're in what's called an encapsulated collection because you don't necessarily want to expose a list from your domain model because then anybody could do whatever they want with the list right they could um clear the list or remove items from the list and you would have no idea about it um so by encapsulating it like this real quick can you make the uh the font a little bit bigger or just like enlarge the percentage down one more time yeah one more time all right let's do 175. there we go is that better yeah that's better i'd make this bigger too but it's that's not yeah that's a that's a bit trickier all right so so this is just a project and the things i want to call out about it is that it is an entity an entity is just something that has identity right so if it has an id and you're going to fetch it and pull it out of the database using that id then that you would usually model that in your system as an entity and so there's a base entity type that it inherits from uh all that base entity type does for the most part is is given an id uh and in some designs it might also give you some like domain events that it's gonna capture for you and then it's also got this marker interface saying that it's an aggregate route and we'll see where that's important in just a minute um when we start looking at the persistence but the intent of this uh project versus this to do item that it has a list of is that it's like a tree where the project is the root of the tree and the nodes under it are these to do items there's a collection of them okay um and so if we look at a to-do item it's also an entity so it inherits from base entity just means it has an id um it it will have in the database it will have a foreign key relationship to the project assuming you're using a relational database uh but that's not necessary to show here if you if you wanted to you could add it as a thing as a property but generally since i'm never going to get a to-do item by itself i'm only ever going to get a to-do item with its parents because it's part of an aggregate i don't even need that id because if i have the to-do item i must have the parent that's just the design but it doesn't have i aggregate root and so what that means is it's communicating my intent uh to other developers to future me when i come back here if i'm working on this and i'm thinking hey i want to just uh you know edit this thing and save it let me take a db context and and you know call save changes on it with this changed uh entity um i don't want it to be able to do that directly i want it to have to go through um the repository and and force it to use an aggregate route as the unit of persistence okay okay and and there's there's more to cover than we have time for on aggregates and design and things like that um but that's the the short version okay so then these also have go ahead another question before yeah we have a couple of questions actually um if you want to answer some before we dive into the next section so first question is can we say that the service layer is the same as the application layer uh it depends on what kind of services they are so you might have domain services that are inside the domain model and they only work on you know domain model types uh you you could have an application layer that has a bunch of services so service layer is not specific enough to me to be able to answer that question uh and if you're building an api or if you're building a microservice i mean when you talk about the service layer of your microservice or of your api that might be your swagger like list of endpoints that that could be the the ui of your whole app um so i don't want to be pedantic but but you know service layer means too many different things to different people for me to just give you a simple answer gotcha uh next question is with ddd and ef core is the unit of work not usable um it's usable yeah definitely uh i don't necessarily have it implemented in this solution template um but but you totally can add it and it will work just fine so what you would do with a unit of work is you would have uh your unit of work would be responsible for actually committing uh any any changes that you're making and you would do the unit of work would be tied in with your data access abstraction which will usually be repositories and instead of you know saving changes directly through the repository you might do a bunch of different sets of work possibly even with multiple different repositories and then use the unit of work to commit them all at once and usually under the covers that's going to be using transactions to to do the work so it's not hard to add that on top of this structure but if you don't need it um it you know it's not in here because you don't always need it it's the short version gotcha uh other question this is related to uh you know using like the base entity as an id returner essentially so sometimes we have an id as a string for goods and other times as a number how can we best handle this sure so this is the base entity that i'm using right now this is what you get out of the box and it is just using an int by default but i've got this comment here that's telling you this could just be a base entity of t if you need to support multiple key types so if everything in your system uses integers then just use this one if they all use goods then just come in here and change this to say good right but otherwise you can say hey i want this to be generic i want to be able to decide what key type i want to use for each one of my entities and then you would do this and now anything that inherits from this will have to pick so if i go look at project here it's going to give me hey base entity is not really a thing anymore it's got to be a base entity of int if you want that one to be an integer and if i want the to do items to be different that's fine these could be base entity of good and as soon as i import the namespace you know that will be fine now this will possibly break other things that are expecting entities to just have uh you know a full type and not to be a generic um but they're easily corrected by just going through and flowing this the same change to those places cool and this is probably something more for the end but before i forget this comment do you have any additional templates that are like the hexagonal architecture that we're taking a look at now i have a lot of stuff on github and on nuget so some of the some of those libraries are being used in here i don't have any other templates except for a clean architecture template for worker service that you can find and it's it's probably a little bit less up to date than this one is but it still has the same structure and everything so if you want to create a worker service you can use that as a starting point right which does work great as like background processes or scheduled jobs uh and so they would benefit from this architecture too if they're not doing something where this is overkill which a lot of times they're simple so they wouldn't need all this cool so there's a couple more questions in chat but i'll let you talk some more and then we can stop and are we okay to go a little bit longer since we started a little bit okay yeah i'm good to go longer if you are cool cool i am down to go over all right so then let's let's keep focusing in on the core and what goes in there so uh looking back at the slide for just a second right we talked about entities aggregates uh i'll cover value objects in just a second uh and some interfaces that might be there okay so i've got an interfaces folder and it has abstractions for things like how to send an email without saying how we're doing it right it just describes what it doesn't describe how so i can implement this with smtp or i can implement this with grid or or some other service and implement those somewhere else in the infrastructure project that we haven't looked at yet um and then there's another i to do item search service this is for a domain service which is defined right down here and it's just got you know a couple of ways that you can go find you know different sets of of items the main reason why this is even in here is to demonstrate this result type that that is another abstraction that you might find useful okay so coming back to uh domain events inside of project aggregate there is this new item added event and i mentioned domain events briefly already they're they're a way to decouple you know things happening uh from where they first get triggered and so in this case a new item event basically just says hey this item on this project was added right and so something raises this event and so if we look at the project it's got a method in here that says add item right and so because we're not just exposing our list of items directly off of our properties we have to give some way for folks to actually work with our our collection and so we do that through explicit methods but in those explicit methods we can add additional behavior like in this case when you add a new item i'm going to add a new item added event and then when i save the project that event is going to get dispatched and a handler will fire in response to that so if we look at this new item event and we say where is this referenced um and i know this is a little bit small but there's a place where it's created and then there's this place where it's created there where's the handler i would expect to maybe i don't have a new item handler all right well there's a two item completed event which is basically the same thing um looks like this just has the item and then it actually has a handler right here um and in the handler it goes and sends the email um to to someone right to says hey someone was watching this and wanted to know if um if this thing was completed so here's your email to tell you hey it was it was done um and so in this case it's hard-coded who it sends it to but in a real application right you you'd have some list of subscribers or something but the idea is that sending the email didn't have to happen inside the project class inside that method so when when the thing was completed it was able to trigger this separately the other thing this lets you do is there's a really common question when folks are building these domain models with with entities and aggregates and things like that here's the uh here's the mark complete method that added that event um inside that mark complete method if you had a requirement that said hey when someone completes this thing you need to send an email to so and so um the obvious way that you would do this is you would put some code you know right right here you say mark it done and then right here send the email and how would you do that well you could just use some static method that sends emails you could pass in a thing that knows how to send emails maybe but though your thing is not a nice to use you might think hey i'll just use dependency injection because that's the solution whenever i need a dependency um but you can't really do dependency injection effectively on entities because they need to be created by your orm right entity framework is going to create this thing and any framework doesn't know how to do dependency injection so it'll just fail if you try and do that and so what domain events allow you to do is have a way to fire off these things that need dependencies outside of the entity itself and so if you know you're going to have to send an email after you mark it complete you just add the event and then the work that actually sends the email is in this other handler and you may have to do a bunch of other things too but maybe you need to log it maybe you need to do other other activities those could each be different handlers that are each very very very focused and simple oh quick question while we're on the handle topic what is guard against null the line that's under the handlebar yeah yeah so this just ensures that this domain event is not null and so it's an example of a guard clause and i actually have some slides on that so let me just jump to the slides and it's a little bit ahead but that's fine uh where is it where is it right there guard clauses uh and we'll jump back in a second to cover those other things but a guard clause is just a really simple pattern to jump out of a method quickly if if you find that it's going to be an error anyway and they clean up your code so that they they don't have a lot of of noise inside the method around doing error checking and basically just invert these checks so if you if you had an order a process order method like this one and it said well if the order is not null if the customer is not null then do the thing right here otherwise throw an exception for customer otherwise throw an exception for order like this is a lot of noise yeah yeah you can't even see what the real work is right now if i had some right you'd see it but it's obscured by all this if stuff um guard claws would say check if it's null and if it is blow up and check if that's null and if it is blow up um and so those are examples of of this the pattern guard clause now if you go out to nuget you'll find that there are some different packages out there and what i'm using is my own our dallas.guard clauses um that define a bunch of common things you might check for and then let you have a very consistent way of doing these checks and it's extensible so you can add your own as well so if you have an application where certain things always have to be true like you know every every method or every every entity always has to have this property on it um but if it's not set properly then something went wrong all right you could put a guard against you know my special case in there uh and with one line you'd have a very consistent way to check for that sort of thing if you look inside the code for guard against null it just does this right but it does it in a in a consistent way so it's the same every time um and later on if you want to change how you do it you just change it into one place and not have to go change every one of these so much cleaner yeah because we i think we've all been there with the nested if statements to check for null or lots of different options yeah if statements are often they're often sort of the enemy right and when you when you look at a method and it's got like five levels deep of indents from if statements uh that's that's the worst like you know you know you need to refactor that and pull some of that out to make it less uh convoluted right all right so those those are the events and the handlers and kind of how they work then the last bit that's in here that i want to make sure i talk about is the specification so if i want to get a project and i want to make sure i get the project with its items right well if i'm using a db context directly i can easily do that in ef core by just saying go give me you know dbcontext.projects you know where id equals this or first or default of id of this oh by the way include project.items and if i needed more stuff i might say then include this other thing right so that's all stuff that any framework understands but you got to remember to do it every time and i don't know about you but sometimes i forget and so i'll create this query and i'll try and fetch the thing and i want to go see what the count of this sub property is and it's blank and it's zero and it's null or it's not set and i'm like why isn't this working i know i just added this thing and it's because i forgot the include um and so ef is not populating that property for me um and that's fine that's what it's supposed to do but i don't want to have to always remember every time that i need to do exactly this or that include um and if i'm using a really you know dumbed down repository pattern that just knows how to like get my id and it just gives you the thing but it doesn't do any includes you know that's often where you get bitten by that it's like you don't necessarily remember that oh wait that's right it doesn't actually give me um all of the related uh collection properties for example or navigation properties even and so this specification basically lets me remember a query like that and then reuse it anywhere in the system and so i have a repository that that i'm using um and it understands how to take in a specification and then it translates that into a query for entity framework and gives me back the the appropriate result and so i'll show you how that works in just a minute um but you you can make as many of these as you want you can name them usually i group them with the aggregate so you know for a given aggregate like a big application might have a bunch of different aggregates right you might have a customer aggregate an order aggregate and something else aggregate and so rather than having top level folders for things like uh events and handlers and exceptions um it often makes sense to group those by aggregate right these events only make sense for the project aggregate and this handler is only for the project aggregate these specifications are only for the project aggregate um so i don't want to put them in in the root level and have you know thousands of them uh it's better to organize them like this i think uh and they can be more complicated right so here's an incomplete item search string that checks for stuff if you've got you know rules where you always want to make sure that your your soft deletes are not included right so you always want to say like you know is deleted equals false or is active equals true or something like that you know those are things you would certainly remember to put into your specifications but someone might forget if they were just on the fly doing some link query in a controller okay so that's how these work uh this to do item search thing uh i think actually uses yeah this has an example of what a repository looks like so here's here's a specification and a repository called get it so this is this is a search service but it's a very abstract search service right it does not know about the database does not know about how it's going to use doesn't even know about any framework right it's it's in the core project because it has no dependencies on any implementation details that's great yeah ignorance is worth sometimes uh and the nice thing about that is that you could totally unit test this right it does not need a database does not need any any real stuff at all and uh and so i've got unit tests for it down here that we could look at because there is some complexity in here right there's try catches there's if statements and so you might want to verify that all this stuff works with a unit test uh and it's it's trivial to do that because it's in the core project it doesn't have any dependencies on those things that make unit testing difficult you don't have to really uh you know worry about it um so the way this works here is you just create that instance of a specification you pass in anything it needs so this is saying i want to get the whole project with its items by id so i pass in the id and then i await you know get by id or sorry get by spec async pass it in that specification and now i get the project and i know at that point it's going to have all its items with it um without me having to deal with the db context directly without me having to write any link stuff here because link is hard to reuse so without copycase yeah again it's so much cleaner for lack of a better word you know not having like at first glance it's easier to understand the function of this particular method in this case without having to go deep into the db context yeah so that's great all right so that's most of the stuff you would care about in this all right so then in infrastructure like the other thing that i want to make sure people take away from this is when you look at a lot of applications uh even ones that use like the repository pattern you might find that they have lots and lots and lots of repositories right so they've got like 10 different entities or 50 different entities and every single one of them has got a repository class somewhere that knows how to talk to it um this is my whole data layer right it has an appdb context um because you know it's using ef core so of course it does and in there it has a db set for each thing and obviously they'll get longer as this becomes a real project with more things but we don't have any more classes right it's just just more properties on here all the configuration is in here so you will add another file for each new entity you add where you configure it that's just because i'm not using the data annotations attributes i want to keep the the core uh very clean again for lack of a better word and so i prefer to put all the configuration of the entities inside the infrastructure project because the infrastructure project is what it's supposed to know about the data store not not the core um and then this ef repository uh is this long right i've got some commented out code here that i'll show you but it's using um a repository a generic repository that ships with this our dallas specification uh nuget package um and so you just add this thing and then you assign it whatever interfaces you want it to use um but it's base behavior includes all this stuff and so the one that we were just using which is the reason why this is still here uh is the get by spec async do i still have that here maybe i don't have it here well there's there's one that takes in a specification that isn't in this comments but i can show it to you in the other project um the the cool thing here is that in this thing i don't need a bunch of repositories in fact i only need one generic one uh and it's it's barely any code at all um and it does all the work so i haven't actually run this app yet maybe we should do that let's run this and see what it looks like again it's a solution template so it's kind of like a hello world uh thing when you run it but just to kind of prove it works here's what you get uh it's got some stuff in here it says like how to use this template with various things uh you can see what the all the apis are so it's got swagger support uh here um it shows you like it has some seed data so you can look at a project there's a project in there called test project excuse me and it's things are not done yet and so if you want to test this thing and actually mark things as complete you can actually go in here and find like here's a list of incomplete items let's try it out with number one and execute and so there's the uh search string is required okay fine so search string is uh shoot what would be i gotta remember what the things were that i can search for how about working that'll that'll work so go back to the home go back to swagger and look for working capital capitalized and try it out with one and i don't remember if it's case sensitive or not there we go and look at that it actually brings that thing back right it is done its fault and then we can mark that as complete so here's an api that uses a patch uh to mark it as complete so that was id number don't know oh it's because it's using in memory it's interesting it's not usually a thing why is it using zero weird um i don't know what this will do honestly usually it's using uh sqldb no such fraud yeah okay so why is it that should be one and one i would think maybe i'm not sure why those ids didn't get populated one and one let's try it just thinking about it oh yeah okay this is this is expected uh all right so it worked uh and it tried to send the email so because we completed uh a to do item it's now trying to send an email and i don't have an email sender set up so right now it's trying to send on my port 25 and you can create a test email center you can use smtp for dev or something similar and it will listen and it will pick that up i do have to figure out why this thing returned back zeros for those that's that's concerning me but everything else here is working just the way i would expect uh and if we had more time i'd show you how to use smtp for dev uh all right so that's that's all that that's the front end let's lastly look at asp.net core since that's the you know type of tech we're using oh can i ask some more questions from chat before you go into that sweet so first question is how can cqrs it's like command query something and the mediator design patterns be implemented in the structure um cqrs is command query responsibility segregation that's what i was looking for was that screen right there all right where you are um and the mediator design pattern you don't have to use cqrs with mediator um but you know they can certainly work together well the idea with cqrs uh briefly is that you want to separate things that mutate the state of your application which are commands from things where you're just asking for information which would be queries and so in this example if you looked at my api i didn't have a lot of things that were both manipulating the state of something and returning you back data so when i made that patch call to update an item and say it was complete which isn't necessarily you know not every rest or whatever implementation would use patch for that but let's just leave that aside when i made that call it didn't return back a query and show me all the items that had been uh updated right it just did its thing and returned okay or not uh in this case it returned to 500 because it blew up trying to send the email but if i had an email sender it would have just returned uh like a 202 no content or something like that um or two of four uh so then if you want to find out the details you would go and make a query now why is that important well it can be useful for scalability as well as simplifying your design you don't have to do as much validation when you're just doing a query as you do when you're actually mutating state you need to make sure everything is valid before you would mutate the state because you want to change into an invalid state but for a query you just need to know what they're looking for and then you give it to them and so it's a lot simpler also the way you architect the back end could be different because your queries could all be hitting a cache or a separate data store that isn't the thing that is your transactional data store that all your commands interact with um and so that's that's another uh reason why you would want to split it and then on top of that on top of like the actual infrastructure of how you store that data there's different patterns you could use on top of it that are helpful for scalability as well so commands are are really amenable to being wrapped up in messages and then distributed over some message queue or message bus and then other processes can process those commands so you don't have to have your app do the work you just need to like encapsulate that command and throw it on the wire it goes on on a queue and something else eventually does the work it's harder to do that if your your application is designed to also do a query right after the command fires right you want to be able to just say i got your command and i dispatched it i'm done now then your queries on the other hand they benefit from caching they benefit from caching as a separate service like a redis cache or something like that you could also do caching in your application you can't usually cache commands and you can't usually dispatch as a as a message a query right because your query the user is sitting there waiting for that response so you don't want to dispatch it off and have it sit on a queue for a while and so those two different patterns dispatching commands and caching queries um work really well as long as you keep those things separated as soon as you combine them then it gets a lot nastier and you can't really use it either one of those patterns so hopefully that answered the question probably with more than they were looking for now that was great the more the merrier uh the next question is what's better ef lit core lazy loading or af core include uh i am not a fan of lazy loading in web applications lazy living is great if you're running a windows app and there's one user and they're moving around inside and you know opening up records and looking at their details and things like that like that's great run that win forms wpf you know single user web app that's running on on someone's computer using lazy loading and that's what it's designed for when you're building a web application what you are having each request do is try and return back to the user's browser or to the the client that's calling your api in zero time right you want to measure it in milliseconds at the most and so if you use lazy loading what that means is that you're going to make more queries to the database and every separate query you make to the database is going to be another round trip which is going to take some non-zero amount of milliseconds to perform and so the more of those you have that quickly adds up so that now your responsiveness of your application suffers and so i always prefer to make fewer calls to the database which means using dot include in a web context um you can very quickly especially if you do lazy loading in a loop very quickly have a tremendous number of database calls uh happen and not even know it with lazy loading and so it may run perfectly fast on your local dev machine with three records in the database but then when you deploy it to production that's when you notice that it's actually making hundreds of database calls on your on your your company's homepage or whatever and why is this why is the site so slow great and uh one more question and then we can go back to that the content so technically you can have n number of classes for each spec that seems hard to major with master detail list and number of classes with each spec so let's go look at a spec i think what they're saying is you know if i try and name this thing project id with items for instance and it has you know 10 different sub collections if i have to create a different spec with every different combination of those things that's going to get crazy i think maybe that's asking yeah that was part of my interpretation okay yeah right so how do you handle that if if we looked at let's say our aggregate project here and if it had a bunch of these types of collections that would not be lazy would not be loaded without it include unless you were using lazy loading um what i would do is i would just have uh since this is an aggregate and since an aggregate is supposed to be persisted as a unit uh in that case i would i would rename this spec to be something like you know project you know aggregate and that's it like if i say it's an aggregate i know it's the whole thing like to me that's that's that's what that means to me if your team doesn't think of that or doesn't know it you could say project id with you know all uh dependencies or all you know collections or all children or whatever right if you just say with all children then it could include the items and the owners and the companies and the whatever's uh all of those things in here and when you add more you don't have to change the name of this thing you just change what all it includes and all of your code still works yep cool anything else uh one more question and then we can go in so lots you're getting lots of questions and what situations can we not use a generic repository class in what situations is that what they ask yeah um very rarely with this approach right if you're not using a specification then there's all kinds of reasons why you won't use a generic repository so um without a specification the problems you run into is that your generic repository let me show you one real quick let's see here's this and if i go to this old one that i have here so here's here's a generic repository um and it looks something like this right it's got get by id and list add delete edit that's that's that's it um and then this one's got this list with an expression which is sometimes handy um if it didn't have that then it'd be even worse right so a lot of them are just get id list add delete edit uh and what often happens is your generic repository falls down as soon as you need any kind of custom query like well i need the order with its items so i can't just use get by id because it doesn't know that it's an order so it doesn't know to include the items so i have to create a separate thing so then you end up creating i don't know if i have an example of this here like here's here's the generic one yeah i don't show the specific one so you can create a generic one easily enough and here's what get id would look like you would say here's your db context go get something of t which says order uh find the id and boom here it is right but what do you get with that you just get the order you don't get any of its children because it has no includes in it um and even if you do the list that this one provides and gives you this where predicate where you just pass in a lambda it also doesn't do any includes uh because that's just beyond the scope of this simple uh version so what you do in that case is you end up creating subclasses of this where you have an order repository that inherits from repository of t and it says where t colon order uh to restrict it and then it adds more methods it says you know get by id with children or or you know list active orders or get orders by customer or whatever else right so you add all these extra things um to it and that just multiplies how many data access classes you have so you've got you know all these all these different data access repositories with different methods for doing these queries specification gets rid of all that because as soon as you have one method on here that takes a specification uh and i can go out to [Music] that project real quick then you don't need to to do any of that right the the specification where's my docs read the docs and getting started can i show an example all right i'll show you the actual sampler so here is a uh sample and it has in core it has some specifications and you do like customer by name with whatever so this is you know similar to what what you're just seeing in the clean architecture like i can define that query and instead of having to have a different repository with all these extra methods on it and they just keep getting longer and longer i just create a different specification for each different custom query i might have whether that's how i'm filtering whether that's the shape of the data i'm getting because you can actually do selects on here too so if i don't need the entire row i only need like these four columns of it i can do projection and selection in here as well um using this using this project using this nuget package and on whether i want caching which this is showing so you can just enable caching here uh pretty pretty simply um and if i need to do includes right all those things are in the specification and that's why the repository is trivial cool yeah that's awesome sweet so let me show you the repository that since we're in here anyway um and and so i think it's i gotta find it repository all right well there's the interface but where's the actual implementation that's the interface uh i know that's the wrong one all right i gotta find it sorry no worries we're a little short on time so um i know you still wanted to talk about the back end and if you want to give like the spark notes version of that as well too the front end actually yeah yeah here's the repository sorry that took me a second um it's it's in the specification.ef core right there's a different version in the ef6 one so if you're still using ef6 you can use this um and the most important one in here is the ones that use the uh the specification so this one right here says you know get by spec async for a given specification um we'll apply the specification to the query and then you know return the result so when you look at the apply specification um there's this specification evaluator and that's that's where all the magic happens where it applies the includes and the where's and everything else all right so that's that's enough for you to know about how that works let's spend the last minute or two that we have looking at the asp.net core this solution template um if you ever do file new uh uh asp.net core project right you which we've all done um you're gonna have a choice and it's gonna say what kind of project would you like um do you wanna have an api or an mvc or a razer pages or this or that um and so i didn't want to have to do that so i gave you all of them and so if you only need an api you just delete the things you don't need if you only want razer pages just delete all the apis okay so here's an api controller and it's got this this projects uh controller in here and it just you know has you know how to get all the projects and and get the id and things like that uh it's got razer pages that kind of do the same thing and show you the projects it's got views that kind of show you the same thing you probably don't want to have pages and views in the same project so pick whichever one you like better and delete the other one and then it also has endpoints which is sort of a better way to organize your api so instead of having this controller that's this big long convoluted thing you have to you know sort through and it gets longer and longer and longer every time you add an end point and none of these things have anything to do with one another right this one never calls this one if i'm working on this one i don't care about this one so that that's the reason why you know controllers aren't really a great organizing uh tool for apis so instead you can use endpoints which are shown here and it's basically one class per endpoint so here's my endpoint and it only has the dependencies this endpoint needs it doesn't have all the dependencies that anything else in my controller needs and the types it needs are right with it so it's kind of like razor pages for apis and this is this is coming from another nuget package i have which is this our dallas api endpoints package and it just makes it so that you can have your controllers uh have exactly one uh method per per controller so probably close to time at this point um yes we are i wanted to show that is awesome this is really really good content i love seeing the architecture stuff that can just help keep your code clean and easily extendable in the future so uh i think the big question is where can people go to get started with both this template and any of the other stuff that you demoed today sure so if you go to nuget.org uh and just search for our dallas a-r-d-a-l-i-s that's me and so you will find all the different packages that i that i have out there here's the guard clauses one someone asked about uh the clean architecture template is somewhere down here it hasn't been gotten nearly as many downloads so it's it's kind of down lower um just updated an hour ago you can see so it's got the latest uh asp.net core dependencies um specification is in here the uh so let's say that we use api endpoints is in here and the result abstraction we didn't talk about much but it's in it's in the project as well is in here so nuget is a good place to just search for our dallas um along with my github repo and if you want to read stuff on my blog go to ourdallas.com and slash blog and you'll find various articles on things like should i use lazy loading and things like that awesome this is again this is really awesome content you got a lot of uh comments and questions in chat that really showcase how useful this is and for questions that we didn't answer um i'm sure they can follow up with you on twitter and stuff right too yep or uh in in the various packages they all have issues and uh you know i think i think most of them have discussion as well or ask on stack overflow and just tag the thing but like there's a discussions tab here that maybe this one's not set up but you know people ask questions on issues too and that's that's fine so great yeah and we'll just honestly we'll probably just have to have you back because yeah i'm sure there's a lot more content that you didn't get to share that would be great for everybody yeah a lot of these packages you could talk about for an hour all by themselves so yeah i believe it it looks like you got a lot of them too so that's great all right so thank you so much again steve and thank you so much to the viewers go check out all of the amazing nuget packages and templates that steve has created under our dallas our dollars.com you said was your where your blog is at as well yep great and i'm on twitter and github our dallas everywhere so yeah and i hope everyone writes some clean architecture and until next time happy coding y'all [Music] you
Info
Channel: Microsoft Visual Studio
Views: 24,051
Rating: undefined out of 5
Keywords:
Id: GYZcds1FjEM
Channel Id: undefined
Length: 64min 20sec (3860 seconds)
Published: Thu Oct 07 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.