An Overview of eShopOnWeb, an ASP.NET Core Reference Application

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
do uh in terms of presentation i have a little bit of an agenda of actually just going through eshop on web as if i were a brand new user uh kind of showing you how to get started explaining some of the patterns that are used how the architecture is laid out and hopefully giving you a good idea of how you could use this application to learn about net core domain driven design clean architecture some design patterns and then maybe take that to your own work from there so let me verify i've got the chat going and the sound is working and everything and we'll get started and see i don't see the sound refresh i think we're good right good right perfect okay i always like to check that so i don't stream for minutes and then find out that nobody can hear me and we've got the the chat got a few people here so let me switch over to uh this view and then we'll show what the website looks like all right so here's eshop on web if you just google for eshop on web it'll be generally the first thing that that comes up and what this is is a asp.net core reference application we'll start by looking at the readme file here real quick you can download the latest 3.1.net core 3.1 version of this ebook for free either as a pdf or you can just click here and it's actually in the docs for asp.net core so you can read through all the chapters and everything right here in your browser and the documentation website the book is broken down into these sections so it talks about how you would build an application this isn't a monolithic app so that means it's a single executable web app for the most part we've actually added a blazer front end to it so it's using blazer webassembly for the admin portion of the site so that is a separate app in essence but it's it's a client-side spa single page application app that ships with this overall the app still is deployed as a single application and not like a microservices architecture there are other eshop variants there's an eshop on containers that is a microservices version of this it's the same functionality it's the same online store but it has a very different uh and more complex architecture that's meant to demonstrate how to use kubernetes how to use message services and things like that to build a more scalable more robust system but honestly this monolithic asp.net core app is what probably 95 of the applications you'll build even if you're working for a big fortune 500 company are going to need right very few apps are going to need the complexity that um kubernetes and microservices and all those things bring to the table lots of developers love to know about that stuff and it's some cool technology but realistically most of your business uh applications that you're writing or even consumer facing apps will get by just fine as a single application that you ship and deploy um as one thing or maybe as a couple of related things maybe a web app and an api app or something like that all right so here in this book on the toc we talk about how to build asp.net core mvc apps uh how to work with data we talk about entity framework core we talk about how to write tests and we'll look at some of the tests in this sample as we get to them and we talk a little bit about azure the book is is not a commercial for azure but it does you know mention azure and shows how you can set this up in azure as well so when you first clone this running the sample is is the first thing you're probably going to want to do make sure everything is working and so this section of the readme ideally is going to help you with that it'll kind of show you how how to get started you can run the sample just by pulling it up in visual studio and hitting f5 to run it that'll run the web app and you'll be able to interact with the store to run the back end for the the blazer webassembly you probably will need to run a separate service that you can start from a command line or you can launch everything with docker and so if you have docker installed you want to just run all the things in docker you can do that as well and so that's what this is referencing right here all right let's see what people are saying on chat garbled knowledge welcome i wanted to ask an unrelated question about eshop on the web it's hoping you're willing to add a.net framework version of it in the sense of clean architecture so data framework meaning.net core and just kind of see what it would look like if you built it with what asp.net mvc5 something like that um one could certainly do that at the moment i'm in the midst of of writing a book for microsoft on how to do the opposite of that and how to migrate from a full framework app to net core because a lot of customers are going in that direction um i don't know that i'm going to have time anytime soon to port this the other way and do it with with.net framework although you certainly could if you're looking for a dynamic framework example of how to do this kind of clean architecture i don't have one that uses this sample but my ddd fundamentals course on pluralsight has basically a sample that uses that structure which is the the veterinary clinic sample so i don't know garbled knowledge have you looked at that pluralsight course let me just google for it real quick okay you watched it and you'll check that all right awesome um if you want the example for that is on github so if you go to github.com dallas you'll find uh that i have a couple of repositories out there that might be useful i think i'm up to 183 it looks like so if we look for ddd in here you'll see this ddd vet sample and i'm in the process of getting this ready to do a new version of the course and it's going to be in core so this.net 5 branch eventually this is going to be the master branch so if you're watching this on youtube you know a while from now i'm recording this in october of 2020 um you may not see the same thing that we see here but i'll have a tag or something in here that'll show the old.net framework version whenever i get around to fully moving this.net 5. in any case today right now this is all fullframework.net um if you go into the front desk solution that's the main thing and you'll see it's organized with core and data and separate web projects there's a few other things in here too that are just sort of separate domains and separate bounded contexts uh in the in the context of the domain during design for this course but they're pretty minimal the main bulk of the demo uh that is shown here is going to be in these appointment scheduling core and data and web projects so check those out if you want to see old school.net framework you know old cs proj with with all the xml and lists of all the files and stuff before we got the beautiful.net core project files all right i hope that helps um okay so let's let's continue on with how to get started with the eshop on web sample um if you want to run it in docker here's the uh pretty much real simple uh you just download everything and go into the root folder where the solution file is and say docker compose build docker compose up and then you're going to want to go to these two locations if you want to get to the web project or if you want to see what the apis look like there will eventually be a link hopefully to this video on this readme as well so you'll be able to jump back and forth between the two but in terms of actually just running it you ought to be able to just clone the the repo or download the repo from right here and then use visual studio to launch the application and let's let's try that out right now so i'm going to go and open a new uh browser or a new command line in here so i've got a scratch folder that i can do work in i'll open up powershell here and bring that up and from within powershell i already have eshop on web in here i might have done this before no i don't all right so i'm just going to clone it so let me grab the url here you can also download it and unzip it and it works too so i've got the the url in my clipboard i can say git clone here now if you want to make changes to this and maybe submit bug fixes or or features you won't want to clone it from here you'll want to fork it first and then clone it so in that case you hit the the fork button up here in the top right of the browser and then you'll be on your page and that's what you'll clone if you clone directly from here and then you find some bug and you want to fix it it's a little bit more difficult to do you have to go back and create a fork in order to do it all right now that i have it you see it's listed here eshop on web so we'll just go in there and then in here i just need to open up this eshop on web solution file which i can do with with code or with visual studio i can run everything right from the command line because it's dot net core so we can just do a net build for instance right here and it'll work if i tell it the solution file i wanted to use there's also a docker compose project file in there that it sees now i have a preview version of dot net five on here um think i'm on master so i think this should work what are we complaining about here oh yeah there's this fav icon issue that's that's just popped up i might have to fix that real quick so we'll look into that there's an issue where if you have two fave icons someone just reported this as an issue i think here building under this will toss error just steve says of this favor account so hey while we're here maybe we'll fix this bug all right so in the meantime let's go ahead and just open that solution file and we can do that with either visual studio 2019 or the preview since i'm not doing.net 5 i don't need the preview so just going to open it with regular visual studio and as soon as it finishes loading here i'll bring it over to this window for you to see all right so here's the app when you first get it uh and i'm expecting that when i build it i'm going to get an error because of that that same fave icon issue ideally you get the same experience when you build in visual studio as when you build on the command line it's not always the case uh but we'll see here in a second it's weird that i'm wanting to get an error here oh and look it's they all succeeded um all right so that's interesting um not sure why it didn't give us an error here when it did on the command line but we'll go with it so let's see if we can run it we'll come back around to fixing that fave icon issue later and here we go look at that so i did ctrl f5 so i wouldn't be debugging but i'm able to run the app and now the app is running we can kind of test it out and see see what it does so on this first home page this is where you browse the catalog of products uh it's page so you can see it's telling us that it's showing 10 of 12 products page one of two this is stuff that's you know fairly common to implement there's a bunch of different ways that you can implement it and a lot of times you end up with a lot of logic in your controllers uh just dealing with how to do paging and how to show simple things like how many pages are there how many items are there total and doing that in an efficient manner without actually downloading the entire database into memory uh in your controller in order to do that so i'll show you how that works in a minute uh you'll see the paging works right we can go next page previous page there's also filtering we can filter on brands so i can say i just want to see uh net content hit this and now we're just seeing only the six products that matched uh dot net stuff uh maybe i only want to look at mugs i can pretty much tell you there's only this one mug here that's net so we can do that i'm not sure we have any other mugs hey we also have the cup of tea mug right so if you'd like to drink tea you should get yourself a cup of tea um all right so all that works the filtering and the paging together on this home page none of that's really rocket science like i said but this sample kind of shows you a way to construct that that is very maintainable and testable and doesn't result in you putting a ton of logic into your controller actions the other thing you can do of course is buy stuff so for that we're going to log in you can see on the login page the two users you might use are demouser at microsoft.com also you could pronounce that dmouser or you can be an admin at microsoft.com and i don't know i really hope neither one of these things actually goes to real people at microsoft.com but we're gonna assume they don't we're gonna use the demo user for this one we'll just paste that in there and the password is pass at word1 this is just a reference app so it's not meant to be super secure here we'll log in and now we are demo user at microsoft.com we can add some things to our basket so you can view our basket we can modify it update it see everything updates here go back to shopping add something else when we're ready we can check out now this doesn't hook up to actual payment provider so checking out is is pretty trivial uh we can come in here and shows us you know a final summary of here's what everything looks like and we say pay now and poof it worked right thank you for your order you can keep shopping if you want but also now we can come up to our view of our our username up here in the top right where we see our user specific menu options and we can view my orders where you can see there's this 3250 and it's in a pending status we want to see the details of that we can click in and see here's here's exactly what we bought again all pretty standard fare stuff you would expect for an online web app the phone is buzzing i apologize all right so from here we can also view our account modify things like your profile information change your password things like that or log out so let's go ahead and log out now the other thing you can do is you can log in as an admin so if we come back in here and we change this to be the admin at microsoft.com and log in we can do all the same things as an admin that we could do as a regular user but we also have this admin menu now the admin menu is going to use blazer webassembly and it also relies on a set of apis that need to be running and they will run on a different port from the web app so by default when i launch this most likely if i haven't already turned on those uh yeah if i haven't already turned on switch this back yeah the the back end i'm going to get a bunch of errors here and they're going to tell me connection refused while trying to communicate to that back-end set of apis so what i need to do is a couple things i can if i want to stay in visual studio i can come in here and i can just right-click on public api i can say debug start new instance i think that will start the right port we'll find out in a minute if not you can also do it from the command line so this is running and it is running with the debugger but you can see now all the endpoints that are used are set up here we can test them out let's hit the the get on the catalog endpoints this is using swagger which they try it out execute and you can see right here we've got these different types of things mugs t-shirts sheets etc alright so we just need that to be running so i'm going to move that out of the way and come back to now come back to this one and reload it and like i said i don't recall if it's on the right uh oh what did you do did you stop running the other one uh visual studio apparently stopped the uh the web app from running that's interesting maybe i needed to run them both at the same time all right well let's let's do the way that i know works that i usually do so i'm going to close this down close this one down as well come back to visual studio and i'm just gonna right click and launch this again uh actually we'll do debug start without debugging get that perfect now we're gonna come back to that powershell window that we had running all right and we're going to go into the source folder and we'll go into the public api folder i'm just going to say dotnet run there and that will run the api on the backend and so now this is notice the the port that it's running on this is the port that the uh blazer app is expecting it to look for so um when i was doing it in visual studio i didn't have it in the right profile so it wasn't going to be it wasn't going to work anyway because i didn't have the right port but this should work right so now that this is running on the public api we can navigate to 509.99 if we want so let's let's just demonstrate that uh localhost colon 509 uh with what was five zero nine nine wasn't it if i forget it already do i need https explicitly there it is okay so it just doesn't have a default page so i have to tell it that i want to go to swagger so here it is running on 509.99 and if we come in here and we go to admin we can see all the admin stuff right so now it works and so this is communicating if we bring up browser tools with f12 you can see some logging here telling you how it's working um we can view the network tab and look at just xhr requests and if we refresh this whole thing we'll see whatever requests it made so you can see that it's making requests to get the user information it's making requests to get the list of catalog brands here's the actual json that's coming back so it's leveraging those api endpoints to build this this blazer webassembly app uh is is running in the browser it's dotnet code that's being interpreted by javascript through webassembly and so it lets you write these types of applications using and net but still get a single page application style experience in the browser right so you can come in and have modals uh and modify you know what types of things there are you can change pictures and do all the stuff you expect in terms of editing these these items inside the catalog uh you know obviously create new catalog items as well and so this is how your your users would manage the store the store owners or or the marketing folks would manage the the contents of the store separately from how actual customers would uh interact with the store to make purchases all right so that's pretty much the whole app from running it aspect let's see what it looks like with with docker since i showed that so we'll close this down go back to our powershell and just hit control c to stop it from running come into visual studio again make sure everything is stopped all right and then in powershell that's where we want to run our docker commands we're going to back out to where the solution file is and you'll see there's a docker compose dc prod and yaml file in here if we go back and look at the readme real quick right here at the very bottom of the readme it says docker compose build docker compose up all right so those are the two commands we want these might take a moment so we'll say docker compose build this is the one that is actually going to pull down whatever containers it needs and then build everything to create those containers and then you'll be able to make requests against the the entire thing once you say docker compose up so you can watch this uh do its thing depending on how fast your internet is that might be faster or slower then you see on my machine and while that's going on i'll see if there's any questions so batman has joined us that's awesome uh stagin co says hi steve can you set multiple projects to be run so you don't have to run the api project manually mainly you can um and i've had mixed success with that on here trying to get it to do that by default uh from from github when you download it just making it work so i can test that out in fact i can test that out right now while docker is doing its thing but the way you do that is you come into the solution you right click you say set startup projects plural here and when you do that you can go and say hey i want public api and web all right so right now it's set to single let's say we want to set it to multiple and we'll come in here and say all right when i start this i want to start without debugging public api and start without debugging web apply it say okay it's great start and it says boom unable to start debugging blah blah blah i can't do that so this has blocked me from doing that if i change that now and i say all right well maybe we can't start without debugging maybe the debugger is feeling sad and really wants to be involved let's just say start with these things we'll apply and say okay we'll hit start now this sometimes you have better luck with now i don't like to run the debugger every single time i want to run the app because it slows everything down compared to just being able to quickly launch it but sometimes you do have better luck with that now in this case it's launching a swagger on four four three three nine uh and i'm pretty sure the uh the blazer app is is not looking for that uh and this one's four four three one five so using that approach and being okay with the fact that you're gonna have to debug both things um i think would work and then the only thing i'd have to do to make sure this would work is uh specify the port properly right so in blazer so right now blazer is going to be looking for the wrong port if i actually run this here log in go to admin uh clear this all right so we see that the the public api is on four four three three nine uh if we hit f12 in here window's too big oh it's way over there that's weird go back underneath hit f12 in here look at the network uh and hit refresh we're gonna see that these are all pending and what are they hitting they're hitting five zero nine nine right you can see that so with a little bit of config change you could get that to work so that that might be a better user experience if you close either one and it stops debugging for all of them um so yeah you could do that all right so docker still go oh and it's done good timing all right so now let's do a docker compose up and this will run these on the appropriate ports i believe uh if we set this up intelligently and in the readme file it tells you that you want to go to 5106 for the web project 5200 for public api so let's go to 51.06 there it is we should be able to log in uh we want to log in as admin to see if the other part works as well um that's not a good sign would that not work yeah some reason login isn't happy right now weird you getting any errors let's try this one more time well let's try demo user no all right sometimes this is a cookie issue so if we clear all the cookies and then refresh our anti-forgery token you can hit admin log in let's do it so something's up with how this is doing off in docker for some reason so i'll have to track that down maybe i'll add an issue for that too hey for sending how's it going so i don't know if anybody else is following along if you want to try that out see if you can do this docker compose build and up and then log in see if you get that same issue i've seen with the with the login um i've seen that before but i thought we had that sorted out so obviously it works if you just control f5 from visual studio or if you debug from visual studio for some reason i'm getting that issue on on the docker version all right so it's probably got something to do with the the domain maybe i'm not sure all right so let's look at the code now so let's jump into eshop on web we are using the preview version i guess of visual studio so this is 2019 preview 16 8 preview 5. all right and if we just minimize the whole solution right now let's just take a look at what's in here first so we have two main project folders at the top one is for source and one is for test and that's a pretty standard organization that microsoft uses for a lot of their stuff if you look in the dotnet github repo you'll see a bunch of the projects are sort of organized like this if nothing else they might not have a source folder they usually will have a tests folder to kind of keep all the test stuff organized separately and so this maps to what's on disk so if we look at the eshop on web how it's laid out in the repo you'll see there's a test folder and a source folder here as well where these things map to all right so looking at what's in source you'll see there are what six projects uh in here which is a few more than there used to be just this past summer we added the blazer web assembly stuff and that resulted in adding uh three new projects so we had to add the public api for it to talk to we had to add the blazer admin project which is the actual blazer webassembly application and then we added this blazer shared project which includes any types that are going to be shared between the server and the client so just by adding that single page application we doubled the number of projects inside of this solution at least in terms of the the main production apps not not counting tests all right so the main business logic for this application is in application core and this is following kind of clean architecture onion architecture portion adapters whatever whatever you've heard it known as principles which is that the core of your application the the main model the business logic should not depend on the infrastructure right so it's following the dependency inversion principle at the architectural and project level uh and what that means is that this infrastructure stuff which includes things like data access and and that knowledge about any framework core right right here you can see from this using statement that we're using any framework core inside of this infrastructure namespace in this project we aren't going to have any references to specific data access technology inside of our core domain project so when we look at core you'll see that it has a few dependencies it's using mediator it's using a couple of new packages for guard clauses and specifications which we'll look at in a moment but it doesn't depend on a specific type of back-end technology like each one of these three dependencies that it has none of them have any infrastructure dependencies right mediator does not depend on sql server or azure and neither these other packages so that's you're okay to depend on certain nuget packages if they don't drag in dependencies on infrastructure with them into your core project in here we've got constants we've got a folder called entities and inside of that we have aggregates for basket buyer order aggregate is a domain driven design design pattern which is a way to group together related entities that should change as a unit specifically they should be stored to persistence or retrieved from persistence as a unit and so there's three different aggregates that have been identified in here order basket and buyer um and so you can kind of see how they're grouped together now when we actually persist these things they will be persisted as a unit so you can't just go and have a db context and edit a basket item independently from the basket that it's a part of right and this is important because it means that the basket can have certain logic in it that relates to all of its items and it can know that that logic is always going to be followed and it's always going to be correct because the only way that anybody can modify the contents of a basket is by loading the basket and working with the basket directly if you don't have that then you end up having to put in a lot of extra if and conditional logic in your code to check that things are in a consistent state because who knows if somebody's added or removed an item or changed an item inside of an aggregate that doesn't enforce those constraints all right so that's the benefit of the aggregate pattern it gives you the encapsulation boundary around this set of classes this set of types and if you don't use that pattern and you just have a whole bunch of low-level entities that map to tables or you strictly just directly talk to tables right then you don't have anywhere in your code to do that work right you end up having to put it in a service or in a controller but if nothing is enforcing the rule that things can only or only are able to modify this code through a certain thing a certain aggregate a certain entity or a certain interface then it's going to be really easy for things to get by any kind of safety measures you put in place and so that's a lot of what domain driven design is all about is putting increasingly uh concentric typically uh you know encapsulation boundaries around things right so i can at the low level c-sharp class level um i can say that this class has a certain amount of encapsulation uh and it has you know maybe some internal or private uh data that i don't want anything to change right like this this basket id is private set i don't want anyone to be able to change that um except if i want to inside this class so that's my encapsulation boundary but then this basket with its items is also another encapsulation boundary right it's multiple classes it's beyond what the c sharp class gives me but the aggregate pattern which i'm enforcing through this i aggregate root interface is how i'm going to encapsulate that logic and say you can't change the children of this aggregate directly without going through the aggregate root all right so let's let's look at how that's enforced this i aggregate root marker interface is important if we go look at my set of interfaces here you'll see this aggregate root it's a marker interface because it has no implementation details so it doesn't do anything but it is a marker that's applied to that type that i can use in other constraints in my system and so if we come down here to the ef repository type this is a generic repository that's responsible for pretty much all the data access that's going on inside of this application and you can see it's generic it works with some type t now what is t well we can use generic constraints to define and constrain what t can be and in this case t needs to be a base entity and the reason why that's important mainly is because base entity has our id property and some of the things that we're doing inside of here rely on having an id and so if it didn't have an id if it didn't inherit from base entity we wouldn't want this to apply to it the code wouldn't work but also it has this i aggregate root constraint on that type so that means i can't just use any base entity right it has to be an i aggregate root when i want to work with this and so what we'll see is uh if i tried to create a repository of basket item which is a base entity but it's not an aggregate route i would get a compile time error it would force me into the pit of success as a developer right i would not be able to do the thing that by design i shouldn't be able to do and that helps enforce my my design constraints at the compiler level which is nice it doesn't just have to be like a coding standard or a documentation somewhere that someone has to actually read and remember the compiler is going to enforce this this constraint and we can show that real quick let's go into the web project and somewhere in the web project we're actually saving a basket uh and there's a few different ways of doing things in the web project um by design so if this were a regular application there'd probably just be one place to do it but if we look at say adding an item to a basket [Music] or even checking out a basket let's look at that so this is a razor page for checkout and it uses a basket service but let's let's modify it for a second and pretend that we wanted it to use that repository all right so i can use an i repository a async repository of basket item here and we'll call this a repo now this is going to need a name space probably so we'll say give it that using statement and then here this says look at this the type basket item cannot be used as a type parameter in the generic ie async repository of t there is no way to convert a basket item to an i aggregate route so that's what i'm talking about where it's enforcing the rule i can't work with the basket item directly i can't add them to the database i can't modify them in the database i have to work with a basket and so if i say i want to work with a basket for some reason it doesn't just because it wants me to rename it um then then i don't get that error right and it's it's going to be fine all right what is this namespace is used oh is it is it in the same basket aggregate i probably just need this whole fully qualified thing because uh somewhere i've got a duplicate why does it think that's a name space throw that on there there you go now you know what it is and i can get rid of that i'm going to delete this in a second anyway i just want to make sure there's not an actual error here so this works fine right as long as i tell it which basket i'm talking about and that's fine because basket is an eye aggregate roof all right so let's get that out of the way um cool any questions the page is in the basket folder right yeah exactly so that's why it's it's seeing this basket and thinking that's my name space that i'm using uh and so if that is something that you run into frequently uh eventually what you're gonna do is rename these um perhaps or or rename them in the other place just so it's not a problem but you can use aliases in your using statements to get around that as well so if that was a thing i really needed obviously i don't need it in this case i'm just i'm just adding it to show this point but you can you can use aliases in your using statement to get around that problem if you need to okay uh let's see a couple questions the page in the basket folder constructor injection okay all right so that's all good so let's go back up to um where we were so we're looking at these entities now a point on here i've seen some folks argue or complain that if you look at certain samples of how to organize code in this way that they don't focus on the thing you're doing they focus on the types of things right and that's that's fair that in this example right here the way i've organized this um they are the types of things right these are constants these are entities these are exceptions those don't tell you anything about the fact that this is a shopping cart application or an online web project you can reorganize these however you want right in fact it's not unusual to take these aggregates and move them to the top level inside core that's very common that's probably a better approach than just having a generic entities folder in the root and then within an aggregate you can put things that relate to that aggregate in it so maybe the exception that i have is basket not found maybe a good place for basket not found to be would be in the basket aggregate right it's basket related so you know we could put that there this duplicate catalog item goes inside of you know some catalog aggregate perhaps empty basket on checkout that sounds like another basket aggregate exception so you wouldn't necessarily need an exceptions folder if you put these into the appropriate aggregate folders and that applies to other things as well if we have domain events defined in here you know we would do that there too you could even take your specifications and if you wanted to you could put them with the aggregates they go with if you like that organization better if you had a really large application right this is just a sample it's not meant to be a real world app but if you had a large application with 100 different entities grouped into 50 different aggregates it might make sense for you to take your exceptions your specifications and put them into each one of those top level folders and have more of a feature folder style of organization and that would work perfectly well right it's totally up to you the the only reason why this is broken up the way it is is more to make it easy for me to go through and explain where things are and how they work on a per pattern basis instead of on a per business domain basis but but realize you're welcome to shift that around right so exceptions it is a good idea to have custom exceptions that represent common known business invariants so for example if you should never be able to check out an empty basket right that just shouldn't happen ui shouldn't let you do it the user shouldn't even be given the option to click a button to check out when their basket is empty so if that ever happens something really broke uh and so that's exception worthy right it's not a normal flow um so i would create an exception for that rather than having an exception bubble up from entity framework or from some null reference somewhere saying you know there was no basket so you know checkout blew up with an all reference exception you want to make it easy for future developers including yourself to know what the heck broke and so by giving it a custom exception that's in the language of your domain right talking about an empty basket on checkout that's your domain language that's not low level c sharp you know index out of range or um null reference type of of language right this tells you very specifically where the problem is so definitely create domain level exceptions where you can to make it easier for you to know what went wrong you can add extensions if they're helpful so here we had some json extensions that are just here because they might be used anywhere and by putting them inside of your application core they're available anywhere in this case you know these are a little bit you know on the cusp of infrastructure because json is like a specific tech but it doesn't tie me to a certain out of process service right it's not like a sql server a mail server an azure this is all still going to happen in memory so i think it's pretty much fine to have inside of corex it's not going to make it any difficult any more difficult to unit test core by having it in there obviously you're going to want to have interfaces for your design and most of your interfaces should live in this core project and so if we just look at some of the ones that are here we've got the i aggregate route which we already covered we've got an abstraction for logging um that we're using that just you know wraps around how we're going to do logging if you ever want to unit test your logging and verify that logging actually happens when it's expected to then it's nice to have your own abstraction for that if you never need to test that logging is working in an automated way with unit test then you may not need this and you can just rely on the i logger of t that ships with the framework yeah jit cipher this is all if you just search for uh eshop on web um you'll find all this source code so this is not in my repo this is microsoft's uh sample so this is net architecture so i'm the main maintainer of this for microsoft but this is a microsoft repository it's microsoft sample application um that i just helped work on as a contractor so here's the the url where you would get this this code so i can mouse over to the chat there you go okay uh so back to the different interfaces um logger if you wrap around logger yourself it just gives an easy way to unit test it if you're ever struggling with unit testing logging do a search on my blog i've got an article that has some tips and tricks on how to make that easy because it can be difficult otherwise just because the way logging works with extensions instead of through actual class methods iasync repository has everything you need to do with a repository everything is async in this case because that's that's pretty much how you want to be writing your your data access code these days ef core supports async for all these different operations so there's no reason why you shouldn't as well inside your repository abstraction let me take just one second to address a common refrain that i hear on the internet which is that why would you need a repository interface when entity framework already implements a repository it's kind of a silly question because the implementation of the repository is not what gives you the value of the repository the whole value of a repository the whole reason why that pattern exists is because it's an abstraction that you own uh for your data access right and so anything that's in the entity framework is nice but i don't own it right it's it's a third-party dependency i want to have an interface that i own that's part of my code that i can do whatever i want with i can make it as big or as small as i want and i can mock it i can create a fake version of it i can replace it with a different implementation all those things are possible when i own that that abstraction if i take a direct dependency on dapper or entity framework or in hibernate or whatever it might be i no longer own that abstraction right and so that's that's where the repository is meaningless right the whole the whole value is just the abstraction the implementations are you know trivial at that point so that that's why you want to have a repository if you think that's valuable if you don't think that's valuable and you're perfectly happy to just code against a particular implementation and that works for you then that's great all right you got services in here as well um you can have domain services or application services that you define inside of core they're generally going to be like a slightly higher level of abstraction than just data access like repository would do services are a good place for you to do communication between different aggregates in your system for example you might have low level things that you abstract here aside from data access so if you want to send an email you might have an abstraction for that if you want to work with the file system you might have an abstraction for that those interfaces would live inside core as well we have an example of how you can extend the generic repository with a specific one so you can create something like this i order repository that implements the iasync repository of order and then adds additional methods to it now we don't really need this this is just here to show you folks like a way of doing something a better approach than this is to use specifications and we're going to talk about those in just a second but this is a common way for folks that don't use specifications to extend a generic repository with custom logic that they need usually it's custom queries that they want to write so in this case this says i want to get to get an order by id but i want to make sure it comes along with the items right i don't always want to fetch the items when i fetch an order but when i do i want to have a way to do that and that's what this method would allow me to do there's an order service it's just another domain service and a couple other utility things there inside services this is where you will define your domain services what makes it a domain service instead of a different kind of service mostly the fact that it's going to operate at the abstraction level of your domain so it's going to take in and return domain level objects entities and aggregates and things typically it's not going to return a view model right it's not a domain service if it knows about the views right if it knows about the ui so you might have services that return view models those are application services they're going to live in a different project okay so here we have a basket service has a ad item to basket delete basket things like that there's an order order service which just has this one method to create an order and in here if we look at what this is doing you'll see an example of how like guard clauses are being used and specifications are being used so the first line here creates a specification we haven't seen what those look like yet but it's next on the list so all the specification is a definition of a query right defines a query as an object and so i can take that specification i can pass it to my repository my repository can execute that query and give me back the results so that's what this does this basket with items specification describes a query and says i want to get a basket it should have this id and hey i want you to include the basket items when you return back the results of that um then i pass that into this first or default uh method on the repository and that gives me back the basket and it'll have its items now i have guard clauses here that verify that it's not empty uh in that it's not null and so if either of those is true then this is going to throw an exception right the guard clause will will throw an exception and the rest of this code won't be hit because we will we will have jumped out at this point so the idea with guard clauses as a pattern is that you want to fail fast and keep your code from having to get cluttered with a bunch of error handling logic right you put all your error handling right up front you jump out of the method with an exception and then the rest of your code whatever's left is the so-called happy path right that's all stuff that we know is going to work because we we already got rid of all the things that might have made it fail um at least for the reasons that we know like the input related reasons there's still the possibility like this call to the database is going to fail for whatever reason but it's not going to fail because basket id was null or basket you know was null right all right so then in here we're going to create another specification this is to go and fetch the the catalog items that we want and then we're going to get a list of those catalog items and then in here we're going to create a query come back and get that that list of all the things we need to put into the order and then when we're done we're going to create the order here this this order we could use a factory if we wanted to to create that order in this case we're able to pass in all the things it needs to its constructor an order must have a buyer id it must have a shipping address and it must have items if it doesn't have those things it's not a valid order and so it's requiring that we pass those things to it in its constructor and then when we're done we just call this repository and say add this thing now when we do that if we go look at the implementation of that repository it's not just going to add the thing to entity frameworks tracking it's actually going to call save changes right so this when this thing comes back from this await it will have actually made the call to the database and updated the logic now there are some some things you need to know about that right so if we look at this code here here's here's our add and you'll see it does the save changes with it same thing for update same thing for delete so there isn't any exposed save changes from this repository there isn't a unit of work pattern being used here now you just need to be aware of that because what that means is that you could fetch something from the database change its property any framework is tracking it and then call add something else entirely right i'm going to add the a new order to the system and that change that you made to customer name that's getting saved here too right because that same entity framework dbcontext is tracking that other change and when we say save changes we aren't just saying save changes for this little subset we're saving all the changes that that have queued up now the reason why this pattern is okay in my opinion to work here is because this is a web application and a web application should basically on every web request be doing one operation at a time and so that's very typical on the rare instance that you're doing a whole bunch of different unrelated things in one web request then you might not want to use the repository as shown here right you could use a different service that does that in a different way and and not use this repository abstraction but for ninety five percent of the stuff you're going to do every web request whether it's an api call or a request for a page is going to do one uh operation as far as writes go it might do several queries but it's going to do one logical write operation and so when we do that one logical write operation and we're done with it and we call add the thing or update the thing or delete the thing it's okay that it's going to save it immediately when that occurs as long as the developers understand that that's what's happening then your code is going to work fine if you do need a unit of work pattern all you'd have to do is take this save changes out of these three places add another interface method on here called save changes or save or sync or whatever you want to call it and then make sure you call that in order to persist your changes all right let's go back to specifications then any questions not yet all right so let's look at a specification we talked about the basket with items here's what that actually looks like so this is using specification base here the specification of t as a base type and using that it exposes the base type exposes a query which is using the builder design pattern the builder design pattern is really useful for building up uh things that have arbitrary you know sets of of properties or or constraints as opposed to like the factory pattern where you have to know everything up front and pass it all in one method call and so builder works here because we want to be able to build a query that includes not just filters like where clauses but also has includes it might have ordering it might have paging it might have all these different components to it and we can chain all of those off of this query using the specification builder all right so in here we're going to use a where clause and it's just what you would expect with a where clause on a link statement we're going to say where b such that b that id equals basket id what is b well b is the basket and we know it's the basket because this is a specification of basket right that's the t that we're passing to it and we can do that in a couple ways we can say given a basket id or given a string buyer id that's going to be their guide of their username either one of those will let us query and get the current basket for a given user or for a given id of basket and then this dot include is what is going to tell any framework core to eager load that that code and include it with that request otherwise by default when you tell any framework just go fetch me this record it does not include related children and if you don't have lazy loading turned on you'll just have a null for that collection or an empty list and so that's not necessarily going to be the behavior you want all the time i don't recommend turning on lazy loading because again this is a web application we want every request to return in as little amount of time as possible when you use lazy loading you're going to have multiple different round trips to your data source as you traverse your object and you say hey i want to hit that property let me see that list of items it has to make another database call to go fetch those items the more round trips you have to make to the database the longer your request from the the browser from the client is going to take for you to return back so if you can eager load all the data you need in one round trip that's always going to be faster than making a bunch of small round trips to go fetch this and that and the other as your code looks for it and for a given web request it should be pretty uh pretty well known exactly what queries you're going to have to make so you should be able to optimize and get as few round trips as possible for that that given endpoint that you're hitting from the web client you can use this for more complex things so the catalog filter paginated specification this is what drives the home page this is how you can see that which brand we have in the drop down list which type you know whether it's a mug or a t-shirt is in the drop down list and then we call this dot paginate and say how many we're going to skip and how many we're going to take so we're going to have a page size of 10 is the default and so we'll skip you know 10 items and then we'll take the next however many items are in the list when we go to page 2 for example there's also a catalog filter specification without the paging why do we have both of those well if you recall on the home page we didn't just have to say how many were on this page we also had to say how many were there total right and so in order to get the total number of items for a given set of filters we have to make a call that's not paginated because if we just did a paginated call it's always going to give us the page size or fewer number of items and so our total won't be correct this lets us get the total number of items that we can then display on the page you get catalog item specification just goes and fetches all the items that match a set of ids and then customer orders with item specifications just demonstrating that you can chain those include statements you can say i need to get uh an order i need that order to include its order items i need those order items to include the item that was ordered and that item that was ordered is an actual uh catalog item right so if we go look at uh in here for the aggregate of order here we can see there's an order right order has a list of order items right here this is a read-only collection so it's encapsulated other things can't mess with it directly they only can adjust it by calling methods here looking at the order item you see an order item has a catalog item order property and so if i want to load that and be able to visualize it for instance on the on the checkout page or on the order details page and see what exactly did i order um i want to be able to include the order items and then include the catalog item order um cali item ordered is right here inside the aggregate and it's a value object that includes the product name and picture uri so that i can display those items on that order history page all right so the the way specification gets implemented is actually inside of the repository itself so if we look at the repository and we look at something like uh give me the the first or default with a specification i don't know why these things are all green uh there's not need to use hey all right let's clean that up okay so probably have too many async awaits in here because this thing uh could just be waited on from outside of here i know that makes sense um clean those up and add that as a fix okay so anyway in here first async first default async what these are doing is they get a specification result using apply specification and then they call first or default on that so the specification result is an iqueryable generally you don't want to return in my opinion an i queryable of t from your repository because that will allow anywhere in your application to modify that query right so basically you end up with the query logic and data access logic everywhere in your app if you make sure that you only return back elements of your domain model you know i.enumerable or list of catalog item or order or basket you know things are in your domain model then you've got that separation right you can say with with confidence all my data access logic lives inside of my repository or in my specifications inside the things that are focused on data access and inside my views and my controllers and my application services there's nothing in there that specifically is working with data access logic using dot where or things like that could you repeat that again don't don't don't return iqueryable from your repository unless you want to have data access logic creeping all over your application that's that's the main point i'm making now that doesn't mean a queryable is evil or bad it just means you don't want a variable to filter out of your data access layer in this case the data access layer is the repository right and so i have a private i queryable of t here that i can call i can use internally but none of my public methods return iquarium so i can get the value of iquariable inside my repository without exposing it outside my repository in here i use an evaluator to evaluate the specification that's this thing that specification evaluator is actually inside of a separate nuget package so if you want to use the specification pattern you can use that nuget package it's the rdalis.specification it's also on github we'll take a look at that in just a minute to see how this works and then in here we just call get query we're going to pass in the the set of things the orders the baskets whatever it is that's my db set as queryable and we pass in the specification and then it just goes down the list and applies all the things on the specification to that um let's see can i go to definition on this right it just shows me here because it's coming from this ef core nuget package so let's go take a look at that real quick just so you can see it um so if we come back to github.com specification in here the the ef ones right here and the specification evaluator is right here we'll blow this up big so you can see it and all this is doing is saying give me give me this query uh and then call dot select on it with the selector and then call include strings and include you know the aggregates uh here so we can do all the include stuff so selection and includes are both ef core things because those will impact how ef builds that sql query the where clause and other stuff you can use with or without ef core so those things are actually in the our dallas specification uh base specification evaluator here uh and so all the other things on this query like implementing where expressions or ordering uh is in here uh paging is also in here skipping and taking so between the two right you can use you can use just specification even if you're not using ef core uh and it'll give you the the filtering and the paging and all that uh and then if you're gonna use it with ef core you wanna use this this other ef core version our dallas specification ef core and what that gives you is the the selectors here and the proper use of the includes right so if you're doing a specification against say an in-memory database or an in-memory collection you don't need to include things right everything's already included it's not that's not how it works include is only used by orms like ef core to reduce how much data they're pulling back all right um any questions on that cool all right that's uh that's pretty much all the different pieces in application core and that's the main business logic for the application right so all the all the different entities are here we saw the aggregate pattern all different interfaces are here we saw the repository pattern the specification pattern those are the key design patterns that are used at this level of the application along with the guard clause next if we look at infrastructure this is where you'll see all the all the actual implementations of these patterns and so tony is asking when should you use specification again um specification is useful as a way to keep your repository from growing out of control and when would it be overkill he asked so specification is mostly used for custom query logic and you want to use it with your repository so that you don't have to add additional repository methods so let me let me ask if this sounds familiar you've got repository and either you're using a generic repository um or or you're creating a separate repository for every single item that you need to be able to fetch right and so you've got different pages in your application that need different sets of data uh and so you start out with just this order repository it's real simple and maybe it's not generic maybe that you think that's overkill so you just create the method you need so you've got like a get by id and a list and then you realize okay well i don't i can't just do get by id i also sometimes i want to get by id with something right so then you create another method get by id with items and there's like well okay but on this other page i also have to display the customer information but i don't need the items there okay well then we'll do get by id with customer right and so now you're just adding additional methods every time there's some other flavor of this this entity that you need to get back um and over time you might end up with dozens of these right i've seen repositories for common things like user or customer where there's literally dozens of different get methods or fetch methods on the repository where it's like get it with this get it with that get it for the state get it for that region get it for this date range get it for that date range combine those things specification eliminates all of that you don't need to have any things on your on your repository that say get with this get by that you know include this include that all that goes away and you just have one thing that says get with specification or list with specification right and any type of query logic selection logic filtering logic include logic ordering logic paging logic any of that stuff it's in the specification it doesn't need to go in the repository so what does it do it means your repository now follows the open closed principle the open close principle is part of the solid principles it says classes should be open to extension we can change how they work we can change their behavior but closed for modification we don't have to actually edit the code and so with the specification combined with a repository now you can extend the behavior of this repository in an unlimited way by just adding more specifications right you don't have to change any of the other specifications right so this specification doesn't get touched when you add another specification for a different way of fetching basket and this ef repository doesn't get touched right this thing should very rarely change because it takes in these specifications on these methods to to get the first item or to get a list of items up here there's a somewhere there's a list an account also the same way you don't have to to ever add another method on here that says get it with this get it with that all right uh john pink says you can see the chat but you can't see the video i just see a black box that says now live uh probably you want to just restart john and you probably can't hear me say that so try refreshing your browser or try another browser um all right placemark kentucky ky when should i not use ef core for db access i may be off topic sorry if that's the case uh hey thanks for the follow-up art sponge um you can use ef core if it solves your problems and if it doesn't solve your problems then you can use something else why would you not use efcor you don't have a database boom don't use efcor you have a lot of stuff you want to do and it's all stored procedures right you don't need to track changes for anything because every time you need to make a change you're going to pass a specific update statement or insert statement that says exactly what you want to do okay then you have core most of its value is tracking changes and creating sql statements that reflect those changes if you're not doing that because you're going to do it all yourself right you've already got stored procedures or maybe you've got a rule you have to use stored procedures then efcor is not going to add a lot of value now you can use the efcor to call store procedures both to make updates and to fetch back entities so you can use it but you can use lots of things to talk to stored procedures so if that's the the place you find yourself in you can question whether or not it's worth using ef core versus another approach to call those you could use dapper you could use uh you know straightado.net and that would work fine as well whenever my queries are complex or join several tables should i avoid using efcor uh potentially right so efcor is really good at getting back entities part of your domain model where you can track their changes and then save back those changes right when do you need that you need that when you're doing write operations you need that when you're modifying the the actual data what about on a search page right typically where you have a complex uh or join several tables you're not typically doing that on the update product form right or on the customer edit profile page right usually you're doing those types of complex joins on some type of a search right some type of uh let me explore what's out here let me find you know all of the products that were you know made in the last 30 days in ohio all right that's going to be a complex join query you don't want to use a repository an ef core for that right that'd be a great place to use a stored procedure but guess what you also don't want to return your domain model for that right there's no reason to return back a basket with basket items or order with order details in entities for that just return back to columns and rows because you're just going to display it to the user as columns and rows right they're not really your entities so there's no reason to use your domain model for those types of queries anyway so yeah feel free to use a separate read model for read-only operations that are by their nature more of a just a data query right and so you can create separate a whole separate way to do that type of interaction that's different from how you interact with your domain model your domain model is mostly useful when you're manipulating the actual state of the system less so when you're just doing ad hoc queries what do you think about the hybrid of ef and dapper you can do that you can do that for exactly what i just said you could say we're going to use dapper for our stored procedure calls or for our ad hoc queries we'll return back you know dynamic objects or whatever you know read model types that we've defined that dapper is going to return and that would be a good way to have like a split and make it very clear like if we're using dapper we're not using our domain model we're just getting read only objects if we're using ef core that's our domain model we're going to be enforcing all of our constraints that's what we're going to save and we're going to use aggregates right and repositories that would be an easy way to make that distinction now that said you can certainly use the fcor to make those same database calls that dapper does um and so if you want to you could you could have uh ef core call store procedures or call you know ad hoc sql just fine dapper has some some features that are nice also but most of what dapper does ef core can also do so just realize you don't necessarily need dapper but i'm a fan of dapper and you could certainly use it um please let us know your idea about implementing unit of work over ef sure so yeah unit of work is a really simple pattern right all it does is it says batch up some work until i say to commit it and so implementing unit of work over ef is really just a matter of not calling save changes until you're ready and so i already mentioned that my default implementation in this sample this isn't what i necessarily use everywhere but for this sample the default implementation is not to have a unit of work separately and that the right operations on the repository just save changes right when you call the thing it's simple and it works and it works well in a web context i wouldn't recommend using this in wpf or or a mobile app where you're doing operations like on the screen before you hit save and push the changes back to the server or any other thick client type of arrangement because you know this is optimized for web this is optimized for i have a quick request coming in i want to return it as quickly as possible so i'm just going to do this thing in return if i wanted to implement unit of work i would take these out of here and i would expose a separate method called save changes right on the repository as another method so i go look at my interface for iasync repository do all these things and then right here it would also have a task save changes or you know task commit work right whatever you want to call it but this would be where my unit of work would be now that only works with one repository at a time and if that's still not good enough right if you've got well you know i've got a repository for the order and i've got a different repository for the customer and in the course of this one request i'm going to be modifying them both and i don't want them to step on each other all right at that point you might need a separate unit of work uh object that unit work object would be maybe passed into the repository when you create it and that unit of work would typically also know about the db context right you might even at some point need to have multiple different db contexts in order to achieve it so it really just comes down to what your requirements are if you need that unit of work functionality in my experience most of the operations that you need to do in a typical business application are just crud operations for the most part right i need to i need to get this entity i need to you know work with it and when i'm done working with it i need to save the changes right if that's the 80 case of what you're doing a generic repository like this works quite well if you have a few places where like this is where i'm going to actually process the order or this is where i'm going to do this big complex thing that touches five different tables and you know a bunch of different entities maybe don't use the repository for that right maybe create a service that is just for that and maybe that talks directly with dbcontext and does all the things it needs uses transactions does whatever kind of complex data work you need to do in the sequence that you need to do it in its own service right just just make your life easy and separate that out from the eighty percent case that's just simple ef core uh fseni says just a small addition read also might go through domain if you have already business logic for aggregating or calculation before they're shown to users you don't want that logic to be repeated in the domain and uiler so that's a good point if if there is business logic in your domain it has to do with how you're going to organize things within an aggregate or how you're going to present that information sometimes you'll have information in there that has to do with how to how to roll up and aggregate that information for the user for display then you probably would still want to go through your repositories to fetch those entities to do that if you don't and if it's just data in a table that you want to display then you're certainly more capable of just using an alternate way to go fetch that data that would use a read model instead of your domain model all right um let's jump back into infrastructure make sure we covered everything there so uh i should talk about the the data context right so this is this is the ef core db context and these are the db sets that it has and this is the the whole code right there's just one on model creating and the db sets and that's it right so it's pretty simple it's a small application so it doesn't have a ton of different entities and types it needs to deal with and that's usually the bulk of what should be in your db context is just however many things there are you're going to have that many db sets in here to deal with now one thing i strongly recommend that will keep your db context classes from getting too huge is pulling out the configuration separately so i'm a big fan of having all my config in c sharp and not in attributes for a few reasons if you notice on these aggregates up here um like in the basket aggregate let's say right there's a there's a buyer id uh there's items let's go look at uh buyer there's another one so where's address okay let's look at address that's what i really want to see so address has you know street city state zip country notice what it doesn't have it doesn't say this is required doesn't say it's got a max length of 20 or whatever it might be right there's no data annotation stuff here now it's not wrong to do that but i prefer it to be a little cleaner like this and not have that in here because those are really just database concerns right if i store this in json json doesn't care how many characters are in my city right my database table is the only thing that cares about that so since that's a data concern i want to put that in my infrastructure project where my data is defined and i'll add that to this config and now in here i can say here's how order is configured or here's how you know basket is configured and so for any one of these you've got this entity type builder of t that you can use and specify you know how you want that set up and specifically since address is a value object it actually lives on order in this case and that's by design right that's because if i if i'm a customer for a few years and i buy some things and then i move and then i buy some more things i want to see where those things shipped when they were actually ordered i don't want it to be a foreign key to my current address that i that i've set right when i update it they all update magically like i want this to be a historic record so so this is a value object that lives on the order itself um and so its definition is inside of order configuration okay value objects are another ddd pattern along with entities that it's good to know and the idea with value objects is that they are fairly simple um and they're uh they're what's the word i want they're immutable so uh coming up real soon when we update this to use uh c sharp uh what do we have to see sharp nine when dot net five um where we have record types this will probably get updated to be a record type so you'll get to see how that works for api do you put those attributes on dtos um yeah certainly because that's their whole purpose in life right so if we go look at some dtos we would certainly expect to see attributes that are related to model validation uh and things like that on dto types because that's that's their whole job right whereas these entities um here their their job is to be a model of the business domain right their job is not to be a wire protocol or to be a definition of a table right they're meant to they're all about the business logic now in this case there's not a lot of business logic here um but in something like order where where there could be more at some point you don't want all the the data access logic stuff to be obscuring the uh the actual behavior that's inside your domain model and again in a real application you'd have a lot more behavior than what you're going to see inside of any sample app like this one so as an example if i can find one let's go into the public api and see where maybe we have some some dtos like this catalog brand dto doesn't have anything like item doesn't have so maybe i don't have any examples here with those what about in shared any models and shared that have them yeah this one's got some so here catalog item on our shared model with blazer um you can see it has things that that specify when it when it's valid based on a regular expression for the price and a range of prices that are valid and what the data type is so this all is fine to put on dto types that are going to be used as a as a wire protocol on an api or as a view model where you can use these attributes to specify how the view is going to render it but because of that because that's its purpose if we're going to follow single responsibility principle its responsibility is to represent data in a way that works well as a wire format or as a view model for a razer page or something like that its job is not to enforce business rules you know arbitrary separate business rules so you wouldn't expect to have uh necessarily a bunch of business logic in here now this has some utilities in it uh that are that are useful but there there's not like business logic in here that says like for this particular customer from this particular region they can only do these things right that that would be more business logic than just low-level functionality here just as um for api for sending so that's a good approach domain model should be clean of data annotations configuration is an ef matter but for ui models data annotations is a great feature in case of nbc all those constraints will automatically be reflected in a generated html in case of localization then annotations are a savior in my experience yeah and faceni's helped on this project and other projects uh he's been a big help um on specification i think too right uh and so you know he's he's got a lot of good knowledge to share as well on here so thank you um all right so since we're looking at the the blazer stuff let's look at the api for a moment one thing you'll notice about this api is that it's using endpoints and endpoints are an alternative to just using controllers and mvc for apis and what you'll see when you look at one of these endpoints like this catalog item create endpoint is that they they chain their model with them right so there's not a separate dtos or viewmodels or something folder in here where you'd have to jump around and find all the stuff related to create instead this this create endpoint here implements this base async endpoint of request comma response as there's two types here the request type and response type and that then corresponds to a single method on here called handle async and handle async takes in a request and returns back an action result of a response so it's an async method so it's a task of action result of response but the response is the main thing that's coming back from us so in here it's going to do whatever work it needs to do in order to create an item in this case it's it's pretty straightforward it's you know here's here's my create new item and do whatever work i need to do to add the picture create a dto for my return type uh set that on the response and then return the response now the response sometimes the response will have more stuff to it in this case the response really is just wrapping that dto but if you want to use that dto in multiple places you can define it separately so it's shared in in more than one place uh in this case it's right here uh in the root of the catalog atom endpoint so multiple ones of these will will share that dto the other thing the response has is this correlation id and so correlation ids are useful on apis especially if you're going to use microservices but even if you're not what they do is they let you set a correlation id either passed in from the client from from your javascript or from your webassembly where you can track it even from the client all the way through otherwise if it's not passed in from the client it'll create it on the server and then pass it all the way through so what does all the way through mean well if this api needs to go call out to another api which calls another api and some sort of you know mesh of microservices that ultimately all return back and then say yes this succeeded or no this failed by passing along that correlation id to each one of those other sub requests you could look at your logs across all those different services and track what happened with that request and so if there was a problem if it was slow or if it failed you'll be able to see exactly where it failed across multiple different processes by passing around that guide of that correlation id um and then the request is just whatever you need to create the item and so it's pretty straightforward these are just dtos not a lot to it the the thing that this endpoint pattern does for you though is it makes it so you don't have a bloated controller to do this work all right everything you need is just right here um let's see if i can zoom out zoom out it's not working zoom to say 70 right so here you can see the entire uh end point it's doing a fair bit of work inside its method um but all of its dependencies are passed in here these dependencies are only used by this endpoint there's nothing here that isn't used by it how many times you have a controller that has like a dozen or more different constructor arguments passed into it and any given action method is only using a couple of them right but it has to have all of them because it has all these unrelated actions on it and so anything that's passed in here this thing actually uses so it's following the interface segregation principle from solid which is to say that classes should not depend on methods that they don't use i'm only going to use the methods on these types that i'm injecting in and you can see them all being used right right inside this method uh by only doing one method this is also going to follow the open close principle once i get this thing working i don't have to touch it again right if i have another catalog item related thing i'm going to put it in a separate endpoint inside this folder and it's not going to interfere with this one so i'm not going to accidentally break this end point when i go to create another endpoint and when i go to work on one of these endpoints right let's just close all our tabs for a moment if i need to make a change on say how how we get by id or how we list page let's do this page i can open this up i can see here's exactly how list page works i don't have to scroll and try and find the right endpoint and be looking at what routes or which or whatever everything is right here and it's easy to find and then where are my dtos for this that i need well they're chained to it right here right i can just hit expand on just this folder and the request is right here and the response is right here now is that some magic visual studio extension you have to install to get this there's actually built-in visual studio behavior to support this which is basically if you name this thing correctly specifically if you name it the same as its parent list page dot whatever it's actually class is then you'll get this behavior right you could use this behavior anywhere you want in your system now if you just use that convention and so when you go to create new endpoints on here you just follow that same convention you say here's the request i'm going to prefix the file name with the name of the endpoint and it'll get associated with it with this treeview style behavior we can collapse it all right if you want to use api endpoints they are a nuget package that you can grab as well so they are on github api endpoints right there and it doesn't need to be that big um and then they're also on you again so that's that's where you can find these and paste them in the chat um all right so that those are just used for that public api it's also using swagger all the swagger stuff is specified in these swagger operation attributes that's just to be verbose about it and to show you how you can do that the routes are also specified directly here these base async endpoints they just inherit from controller they're not some separate standalone way of doing things they really are just asp.net core mvc controllers but what they're doing is they're constraining you to just have this one method this handle async method now because this thing really is a controller you could actually add additional methods on here and they would be treated as action methods but you shouldn't do that so don't do that um there's actually a set of analyzers you can download um roslyn analyzers that will check and see if you've done that and then throw an exception actually a compiler warning if you have that so you can install that as well if you want uh into visual studio to make sure that you don't accidentally uh do that um but but you never should so any anybody have any questions about the api endpoint approach i've gotten a lot of good feedback on using this from folks that have done it there are folks that that aren't interested in it because they're like why do i need this i can just write controllers with only one action on them now and the answer to that is yes you could so why aren't you right i mean like if big overloaded controllers weren't a problem we wouldn't need api endpoints right if everybody just did them the right way we would be fine but but over and over and over again at different clients i see controllers that are out of control doing way more than they should having way too many methods on them and just being this huge bloated mess of stuff that inevitably has concerns in it that don't belong there so this provides a way to to get around that uh and and it's a little bit of a silly argument when they when you say why would i do that when i can just do it already with with a little bit of discipline um because everything about higher level languages is about constraints right that's that's why we have private on a class is so that you don't go and touch the internals of this class and i can have encapsulation i could just use a naming convention and tell you hey don't touch these things because you're not supposed to because there's a naming convention and if everybody followed the rules we wouldn't have any problems but we live in the real world and that's not how it works and so it's nice to have a compiler that will enforce those constraints and make sure that people do things the way they're supposed to so that we can have um some type of encapsulation boundaries and things like that and so that's what this does right it's a constraint that that helps you do better would you add a producer's attribute to these you could um if you wanted to i don't know that you need it if we go look at swagger uh real quick let me just launch it from here so i set a startup project i think it gets everything it needs from the action result doesn't it what is producers going to give you that that wouldn't give you um here we are so inside let's say this uh catalog item i want to get a catalog item what does that produce it should be right here this is telling me what i'm getting back right so do i need uh what would producers give me as different behavior here otherwise i haven't checked the implementation of the package how's the routing is fixed fixed where's the rule that list page is a route name uh in api endpoints there's nothing to it i'm just using the default stuff right um if we look at an api endpoint base endpoint i'm not doing anything except applying api controller api controller just says it must have a an attribute route that's all um inside the code for this page there's my route right there there's nothing that's forcing that to be a certain thing and if you don't like the fact that this has magic strings in it this could certainly be composed of a constant or something or a name of uh you know name of this name space value or something like that if you want that to be more convention based and have fewer magic strings similarly these things could be built if you start building this out of out of strings you can build these out of strings too um does that answer your question for setting where is the rule that list page is route name i don't i'm not seeing what you want hey john callaway how's it going previous question is related to analyzers let's see what previous question tony davis's previous question did you add produce oh i see would you add produces attribute to the analyzers uh because this who's here i mean you could i think this is mine i think i made this uh that was my name on here i seem to recall making this this graphic a few years ago um yeah if you if you wanted to add a produces to it you could if you wanted to install this analyzer uh to check and see you'd say include open api analyzers true and this is already built into the net sdk it should give you a warning or something that says hey you're not telling us about something you can produce so if you say here i return not found and nothing up here says that you return not found then this would this would help you and for instance if i were to do a get by id here then that should have a produces 404 because that's what should happen if i pass it one that doesn't exist we can check that out by saying try it out with negative one execute and i do get a 404 but it's undocumented so i think that's what you're referring to right so the analyzer would require um would tell me that i'm missing that attribute and then the attribute i would add so in uh here for like get by id of a catalog item here i could say here produces whatever type um let me just verify what that syntax look like type of the thing and then whatever status kit so if i wanted it to be a what this is going to show me it's right there status type just the 404. so produces status code that's called result i don't remember gotta watch it wait for it status quo it's dot status okay status codes dot says four four all right status codes dot all status like that so that's what it's telling me that i can add i'm going to build did i answer your question um chat's getting late in a bit yeah i got it didn't notice the manual route definition the magic string okay cool uh can i convert it from into string other problems so reduces status codes dot i thought that's what this was doing oh produces response type that's what it says sorry produces response you'd add that and if i turned on the the analyzer for open api it would it would yell at me and tell me that i needed that because it would see this line and so i don't have that turned on for this but i probably should at some point and then update it with these all right so tony davis hopefully that answers your question if you want to turn on the analyzers for uh making sure you don't have the uh you know other other actions on here i think that might be on by default actually i'm pretty sure that's in here uh analyzers right there so i think i think these come in with it but phil philip pittle did this and so i think i think you get this by default when you install the package so it just verifies that you only have one public handle method on an end point that you don't add additional handlers to it which you shouldn't do all right uh john callaway thanks for answering your questions on specifications we're getting a lot of mileage out of that package good glad to hear it um i think it's i think it's pretty useful too that's part of why i pulled it out of the eshop on web sample it used to be all of it was just in here uh all the code was in here too to implement specification and it seemed a lot of people were downloading this just to get to that code and use it in their own system so um made it much easier by just letting you pull that in as a separate nuget package uh and for senny yeah he's been a lot of help on there too cool all right um what is left i don't want this to be too long because it's going to go on youtube and it's already you know more than 10 minutes which is about what most people can tolerate on youtube for length i think you should have everything you need at this point there was an issue that this was actually related to which was right where was it i don't know it's one i just answered recently this i got too many issues is the problem um there we go yeah it was this one so 21 hours ago i said hey today i'm going to go and talk about this so what all did we want to talk about well john i don't know if john's here wanted to know whether or not we should use the ef repository of t here uh which is the one we've been looking at that's an i async repository of t blah blah blah or in specification there's a very similar one that looks like this and this is just uses a sample right and it's it's very similar it just says that your type has to be a class and it has to implement this identity of int and the reason why it uses that approach is because it's a separate nuget package so it doesn't know about whatever base types you might be using um and it's it's intended to just be a sample it's not intended for you to have to use it but his question was which one of these should i use um which is fair and so in here we take this specification evaluator and we use it in here to do the work and then here we also knew up a specification evaluator and do the work there either one of these works the important point to note is that both of them are privates that return that iquariable so you want to keep that private don't expose the iquariable in your repositories return types for the different methods and whether or not you want to pass in the specification to the evaluator using this new keyword here or you want to pass it in using this uh this interface and we just knew it up there like i don't really see a big difference there if you're only using this thing in this one place it's it doesn't really change it any um the main difference here is is up here i think uh and the only difference with that is whether or not this this id type is going to be specified on an interface or specified on a base type um and it doesn't matter right either one will work if you want to use that aggregate design pattern that we talked about toward the beginning of this video this eye aggregate marker interface is nice to add you could add that to either one of these and it would have the same effect of just restricting your repositories so they only work with aggregate roots john you are here great um do you have any questions that i haven't answered because i'm going to try and uh pull this down tony davis even if i don't publish it to youtube it will be on twitch immediately in fact twitch is even a nicer experience because all the chat is right there in the window with the video now the reason why i don't just use twitch for everything is because for some reason they only keep them around for about 21 days and then they delete them apparently they don't want to have the ad revenue and they'd rather give it all to youtube so um i just try to make sure i publish them to my youtube channel so that they're not lost um but if you go where to look to see them go to youtube.com dallas that's where they eventually get published so i'll share this in the chat too for anyone that needs it and then on twitch which you're probably watching us in right now you'll see recent videos so twitch.tv slash on dallas quiet somewhere around here is videos i think and where are they um this is really uh difficult to follow right um is it because i'm streaming it doesn't show it maybe when i stop streaming you'll be able to see the videos uh but i know i know you can typically see videos like if i go look at clarkio oh he's streaming let me find somebody's offline if you look at this person there's videos right there right there should be a videos link on mine somewhere right uh so i'm not seeing it but you you'll see it you'll see the videos somewhere and you'll be able to see it there all right um yeah so smart play smart ky he's got the the uh the answer why i don't just leave it all on there um all right so just to wrap up uh oh and i haven't talked about the clean architecture solution so i should talk about that uh yeah and youtube you lose the chat no because i i put the chat in the in the video um i would like to not have to do that um but i think if you're watching this on youtube later and i'm answering questions in chat you're gonna have no idea what i'm talking about if you're only seeing my half of the conversation and i don't have to read every single chat so in the video like right i can't even put my mouse here but right there you can see the chat right in your video that's there in uh in youtube as well um because i've put it there right if if if uh twitch ever gets to the point where they just keep the videos forever and then i would stop doing that right because it's just taking up real estate on my screen and i would just use the whole screen for code and maybe my little picture here but the chat would be in the side of twitch instead of having to be in the video itself that would be a lot nicer so tell twitch stop deleting videos and just host them themselves and then i will make that change right you guys have the power to make twitch do that i'm sure okay so let's wrap this up um he answered this question hopefully if anybody wants to follow up on it before i close this issue uh it's this eshop issue 415. um john if you still have questions feel free to post them there also where to go to find all this code for this it's all on eshop on web it will be updated to net5 probably fairly soon um sometime in the next couple months uh and that's a pretty straightforward upgrade it's already on 3.1 so look for that sometime fairly soon you can download the e-book again right here the ebook covers more of the principles behind this so if you're trying to learn the principles and the patterns and not so much the code go look at the ebook if you want to walk through of the code that's what this video was all about so hopefully that was helpful if you want to start your own application using the same ideas as this sample but without all the sample stuff right you don't have to delete and work around all the stuff that's already in there from the sample i have a clean architecture solution template so go to github.com or dallas clean architecture give this thing a star so you can find it later and you'll get notifications when there's updates which is not all that often so it won't spam you but but star this or fork it so it's in your repo and you can always find it later that way but it has just the basic structure that you need you can download it as a project template and install it in visual studio so you can say file new clean architecture project in there if you want and if you look at this you'll see the code is very familiar looking right it's got a core it's got an infrastructure it's got a web there's a shared kernel project that eventually you would pull out shared kernel is another ddd pattern that's meant to be how you share code between different bounded contexts between different applications and so a really good idea when you're using that is to make it a new get package and then have it be a new package that you reference from your core in any other projects but definitely from core typically of your different applications this thing also uses specification it also uses domain events so domain events is another great pattern eshop on web does not do anything with domain events it's not part of that sample but it is baked into this sample so if you want to use domain events and see how they work you can play with them in this clean architecture template or just delete them if they're not something you need right it's meant to be uh pretty small in terms of what's here that you would need to delete all right um so with that i'm going to wrap it up and yeah i'm sure i'm sure uh placemark i'm sure it's a storage space thing that uh twitch probably figures that like 95 of the stuff people are recording is just crap that nobody's going to want to look at again so they wouldn't get ad money from it youtube apparently is okay with that though so um somehow youtube makes it work uh and storage cost is so cheap these days that i'm pretty sure amazon could afford it amazon bought twitch so they have this whole s3 thing that costs about you know a billionth of a penny per gigabyte for you to store stuff so i suspect they could do it um they just need to get their marketing and advertising team up to snuff right youtube lost billions of dollars before it started making lots of money now it makes tons of money right i think twitch needs to go that route too or and i suspect that that's their plan um they just you know there's smarter people and me involved in running that i'm sure they'll they'll figure out when is the right time all right i'm going to go ahead and stop this recording it's a little bit early today i'm not going to jump us to anybody else i'm not going to raid somebody today just because i want to wrap up this recording so when it's on youtube it's all clean um if you guys have any other questions follow me on our dallas everywhere you can always reach out to me just on twitter here as twitter.com dallas and i will answer your questions there as well or just leave an issue here on github for eshop on web or for clean architecture or whichever project you're interested in all right thanks a lot have a nice day i'll talk to you later
Info
Channel: Ardalis
Views: 17,100
Rating: undefined out of 5
Keywords: twitch, games, ardalis, stream, dotnet, visual studio, c#, asp.net, asp.net core, ddd, blazor, webassembly, eshoponweb
Id: vRZ8ucGac8M
Channel Id: undefined
Length: 105min 14sec (6314 seconds)
Published: Tue Oct 27 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.