 All right, everybody. We're back with Steve talking about the eShop on-web ASP.NET Core application. Take it away, sir. Hey, everybody. I know we're running behind. Sorry about that. Looking forward to getting started quickly and hopefully getting us a little bit back on schedule. I can't guarantee I'll be done in 19 minutes, but we'll be quick. So let me start by sharing my screen here. Figure that out. There we go. Share screen. You should be seeing a slide. Let's see. We're all good, yeah. All right, looks good. OK, so I'm going to talk a little bit about clean architecture because it applied quite a bit to the eShop on-web sample. If anyone has any questions for me and we don't get to it today, all my information is right here on this slide. You can reach me on Twitter as Ardalis. I'm also on Twitch as Ardalis as well. If you want to follow me there, I usually stream on Fridays. You're welcome to follow up with any questions you have there. We're going to do this talk in under 30 minutes, hopefully under 18 minutes now. So if you want to grab the reference app, you can go here, ak.mswebapparchitecture, or you just do a Google search for eShop on-web. It looks like this when you run it. There's a free eBook. It's about 140 pages. If I remember right, PDF that you can download, or it's available on the awesome Doc site that Myra and her team work on. So you can find it online as well if you don't want to grab the PDF. Also, I just wanted to mention that this week we just launched a preview edition of the architectingcloudnative.net apps for Azure Book, which you can find at this URL, which someone will probably be nice enough to throw in the chat for me. Okay, so I have a whole slide deck that takes about an hour at a conference to talk about clean architecture and principles and things like that. I'm going to skip to the end and talk about just some of the most important parts which have to do with dependencies. And so if we think about how dependencies work in a typical old school end-tier application, you've got something like this where direct dependencies because you're newing up something or you have a static call you're making look like this on the left where you have a class that references another class by newing it up or making a static call on it and it references another class, right? And so then at runtime, it runs the same way that it compiles. If you use the dependency inversion principle, which like I said, I normally cover in this talk, it looks more like this. So at compile time, you reference an abstraction and interface. And so at class A looking here at the left references interface B and class B might implement it. Meanwhile, class B also needs to use some functionality it references an interface as well which might be implemented by class C. Now at runtime, your control flow runs through those classes just like it did before but now because we have those interfaces we have seems that we can use to break apart how the application is constructed. I can compose it modularly at runtime from different implementations. And I can also use those seems to make the code a lot more easy to test. Okay, so one of the goals of clean architecture and for the eShop on web reference architecture is we wanna make the right thing easy and the wrong thing hard. We wanna force developers to kind of fall into doing the right thing without thinking about it. And one of the ways that we do that is we construct the solution in such a way that the dependencies flow the way they're supposed to. And if you try and go the wrong way, Visual Studio or the.NET Core build system will stop you because the references just don't work that way. All right, so one of the things that is a principle that we wanna follow is that our UI classes shouldn't depend directly on the infrastructure classes. That's gonna make them more tightly coupled. It's gonna make them harder to test. It's gonna make it harder to swap in different implementations of infrastructure. For example, we just heard about Cosmos DB. If instead of Cosmos we wanted to swap that out for a different data store of some sort then we would have a hard time doing that if directly inside our web project we were working with Cosmos DB APIs. Another example of this is that we want our business and domain classes not to depend on those infrastructure classes for the same reason. Let me just check the chat here for a sec and make sure everything's okay. Yeah, all right, looks like we're good. Okay, the other thing is if we have a lot of repetition in our code. If we have the same like DB context calls in every controller over and over and over again that kind of repetition makes it harder to change directions to swap in a different implementation and violates the don't repeat yourself principle. So are there patterns that we can use to make it so that it's a little bit easier for us to use these things in a repetitive, a less repetitive way to reuse them more often. Things like query logic, validation logic, et cetera. Okay, now I loosely talked about these dependencies but here's an example of the issue you get if you don't follow clean architecture as pattern. You end up with a data layer that talks to a database typically. You have a business layer that calls the data layer and a UI layer that calls the business layer. This is your traditional end tier and these transitive dependencies mean that everything depends on the database which makes it really hard to test at least in a performant way. So everything depends on the database. The clean architecture which goes by other names like onion architecture or hexagonal architecture or ports and adapters. This is probably my favorite diagram that kind of shows how it all comes together. So in the middle you've got your business rules. That's your domain layer. You might also have a small application layer that sits around that that has some rules for how your application specifically works. It's gonna expose these ports that other things can implement that are outside of that core application layer domain layer. What is a port? Well in .NET a port is something that you can implement. So generally it's gonna be an interface. So if we look at that top right purple icon in the internal persistence port that might be a repository or some other interface that you would use for storing data and retrieving data from wherever. And so in your infrastructure layer you might have a SQL server or other database implementation. You might have an in-memory one that you use maybe just for testing or for demos. And you can swap those in and out easily because they both implement that same port. And you can see there's other ports used in this example as well. Okay, so before we jump into the eShop-on web let's talk briefly about the different projects involved. In eShop-on web there is an application core project. And the things that go in there are these types of things. We're gonna see interfaces. Most of those are gonna be in core. We're gonna have entities, aggregates, value objects. These are domain-driven design patterns that their implementations typically live in the core project. Any domain services which are, you know, services that are purely business logic should be in core as well. If you're gonna use custom exceptions this is where they would be defined so that they're accessible from anywhere in your app. If you're using domain events and event handlers which is a great pattern for you to investigate they would also be inside of the core project. And then specifications which we're gonna look at if we have time today are another thing that would live inside of that core project. Okay, so now what goes in the infrastructure project? Remember the core project does not depend on infrastructure. The infrastructure project depends on core. Okay, so inside of infrastructure you're gonna have repositories if you're using that pattern. Repositories are just implementations of an interface for persistence. And so if you're using entity framework and any framework core in this case that's gonna live inside of infrastructure as well along with repositories that might use it. If you're using caching which I strongly recommend that you do your cached repositories would also live inside infrastructure and they would have a dependency on whatever cached provider you're using. This might be Redis, this could just be in memory, et cetera. I see there's some questions on the chat about whether or not we're gonna do Blazer when we're gonna update the 3.0. Those things are all on the roadmap and should be happening soon. I don't have a deadline yet but I would hope to have them done before 3.1 of .NET Core comes out. If you're consuming external APIs then you've got web API clients would be here. Anything accessing the file system if you're using custom logging adapter, implementations those would go in here. Anything doing email. Things that reference internal infrastructure like your system clock even. A lot of us don't think about that but that's infrastructure as well because it's outside of your program running inside your memory space. Another one is configuration files would often be something that you might put the accessors for or the logic for in the infrastructure project. In .NET Core we typically can just use the built-in configuration stuff so that isn't one that's in this particular example but if you're using traditional full framework .NET you might wanna put your configuration code inside of infrastructure. And you might have other services here as well and occasionally interfaces. The only interfaces that should be here are ones that are tightly coupled to some kind of infrastructure. So if you are using let's say an Azure SDK and it has a custom interface that returns back Azure specific types that interface should live in infrastructure because it depends on that SDK and so it shouldn't go inside of your core project. All right, so then what goes in the web project? The web project is your UI front end for this application. It should have mostly things that have to do with web stuff and not things that relate to business logic and that stuff that relates to other kinds of infrastructure. So you're gonna have controllers, you're gonna have views, you might have razor pages depending on which technology you're using. We actually have all three of these inside the reference app eShop on web just to show you how they all work and that you can use them all together if you want. Your view models or API models or binding models, these DTO types generally are going to live inside of the web project as well. With some exceptions, occasionally you might pull them up into the core project if it makes it easier for you to use them from other services. Any kind of MVC filters, model binders, tag helpers, all that stuff is very web specific. It should go in the web project and then you might have other services or interfaces that just don't belong anywhere else. So application services, they might live in the web project because that's the front end of your application and that's the example that we use in eShop on web mostly for simplicity purposes. Likewise, interfaces that go with those application services might live there as well. Another question about .NET Core 3 on the chat. Yeah, expect that to come, it's just not there yet. All right, then there's another thing that you might have. This isn't actually in eShop on web but if you had multiple different solutions in your organization, which many of us do and you wanna share code between them, there's a concept in domain-driven design called shared kernel and you can use shared kernel to share things between those projects. My recommendation is to use NuGet packages for this and you can put any kind of shared types in there. So base classes, common exceptions, common authentication user classes, guard clauses, all those kinds of things might go there. All right, so when you're done, here's pretty much what the structure looks like. All right, you've got, your web project depends on core, your infrastructure depends on core. You have different tests that each focus on these different projects. Now you might have unit tests for all of these things but most of your unit tests are gonna be against core and most of your functional tests are gonna be hitting web and most of your integration tests are gonna hit infrastructure. It's typically how it plays out. Now web has a light gray arrow pointing to infrastructure because the dependency on infrastructure should be minimal. The only place in your web project that you should know about infrastructure, if any, is gonna be in that startup CS that wires it all together. All right, here's the folder structure. We'll look at that in a sec. Let's go to the code. All right, so here's eShop on web. Let's stop this for just a moment and we see the project has this application core and I pretty much already ran through all the stuff that's in here. Infrastructure has the data and then there's the test, the web project. Let's start by looking at the homepage. The homepage actually uses razor pages. So this is the pages folder. There's an index.cshtml. That's the main page for the site and then this index page that has the on-get, if you haven't used razor pages before, that's what's gonna handle this page, all right? So this is essentially the same as having a home controller with an index.action method that gets loaded. So if we run this and just do a quick look at the application itself so you can see what's there. Oh, I already had it up somewhere. Let's go over there. All right, so that's the thing on GitHub. Here's the actual page open on my other screen. So this is the app running. The homepage supports paging, it supports filtering. So if you just wanna see mugs, you can do that. If you just wanna see different brands like .NET, you can see that. You can log in. There's a built-in user here, this demo user at Microsoft, this pass at word one, log in, now I'm logged in. I can add items to my shopping cart. I can update their quantities and check out and I can go up here, I can view my orders. I can see here's my order I just placed right now. I've got five minutes left. I can click on details. I can see everything about what I bought. That's pretty much it. There's no payment providers, it's just a demo app, but it sufficiently shows that level of complexity inside the application. All right, so now let's show how this works as we come into the application. So on this first page, let me turn this off for a second so you can read it easier. The first thing we're gonna use is this catalog view model service. This is an application service. And the reason why it lives inside of the web application is because it returns a view model. So this catalog model is the view model that's gonna return. Since view model is a web-specific thing, it didn't necessarily make sense to put the service inside of a different project than web, okay? So if we turn this back on as a breakpoint and just walk through what it's doing, we'll be able to see kind of how we go through these different layers and how they're related to one another. So start up, there we go. And we should hit our breakpoint, here we go. And we're gonna step into this. And now we're in the Cache Catalog ViewModelService. All right, so we're using caching. And the first thing that's gonna happen here is, when you get to the key map, we're gonna create a cache key based on the different things that we need to know about this request. In this case, it's page index, items per page, brand ID, et cetera. If we find it in the cache, then we're just gonna return it right away. In this case, I just launched this thing, so it's not gonna be in the cache. So we're gonna step in to this one, and it's gonna take us into the Catalog ViewModelService here that's actually gonna get the catalog items. So this is like the real one, not the cached one, all right? So stepping into that now, we are inside the getCatalogItems actual method that knows how to go and get catalog items. So let's look at what this is doing. We can see it's got a little bit of logging, and then it has a couple of specifications. Now I mentioned that we would talk about the specification pattern here in a moment. This is what it's doing. Specification is a pattern where you define a query as an object, right? And so in this case, instead of having a Lambda expression right here, or worse, inside my controller, inside my razor page, I've defined the query that I wanna make and put it into its own object, which is this catalog filter specification. So if we jump into that one, we can see here is where that Lambda lives, right? So this is using a base specification type, and it's just passing in the Lambda to say that, you know, the brand ID and the catalog brand ID have whatever value, right? So that's the where clause that it's gonna generate. From there, we're also gonna get the paginated specification here. So it's just another specification that knows how to do the work to get the paging. And then we're gonna do a check to get the list, and then another check to get the count, because we need the count so we can display the total number of items. And then we just loop through the page, grab the pictures that are associated with them. So there's a bunch of those. So we'll skip past that. And then we build the view model, right? So that's the main responsibility of this service is go fetch the data and then go build the view model and then return it. All right. So looking at all this, we return that view model. Everything comes back out, it stores it in the cache, and we're done in the page displays. All right, if we hit it again the second time, and if we're fast enough and we drill in here, what's gonna happen is we're gonna hit this and it's gonna say, oh, it was in the cache, and it's gonna pop right out. And the results gonna take like almost no time. And I must have hit something to edit something. So we'll stop that. All right, so let's talk for a little bit about the specification pattern, because I think it's one of the more interesting pieces here that most people haven't used. I mentioned, let's see, let's look at the customer orders with item specification. So here's an example of a specification. One of the challenges that you have when using ORM and a repository, and the reason why a lot of people don't necessarily like using a repository pattern is because you can't always easily describe the query adequately, right? Sometimes the repository implementation hides how you're gonna get the data. And so what specification allows you to do is avoid that by saying, you know, I can actually be very specific about the actual query I wanna run. In this case, it's very simple. Get me the items for this buyer, as well as the shape of the data I wanna return, right? So in this case, if I need to include some related tables and not use lazy loading, because that's evil in a web app, I can go ahead and I can add these include items here. If I wanna add filtering or paging, or not filtering, if I wanna add paging, I can do it here with one line. I can just say, apply paging. So what does my repository look like? Because a lot of times what happens when you have a repository is you end up adding a bunch of extra query methods, right? So you've got like an order repository and it has a list method. And then you have another one that says, get the order items with paging. Get the order items with filtering and paging. And you end up with 10 additional, you know, query repository methods, all for different custom queries you might wanna run. This is my generic repository for the whole application. It doesn't have any of those special types of extra methods on it, right? It just has your typical CRUD methods on here. The only one that's probably, one that's not typically in most repos that I see people use is this count one. But this list all async with a specification, that's very, very flexible. That's what's used by just about any query in the system that needs to pull back data. And so this uses this apply specification helper method, which is just down here. And it uses a specification evaluator to construct a query inside the repository using all the features of the specification. And what are those? Well, they include adding the criteria. So we just say dot where, and we add the criteria off the specification. That's that lambda that we saw. And then we can do includes. So here's how you add all the includes from the specification onto your query. Likewise, you can do ordering and grouping and then paging as well if that's enabled. So this one place evaluates everything on the specification, turns that into an appropriate query on the DB context uses Iquariable to do it, right? Everything in here is using Iquariable, but the repository itself doesn't expose Iquariable. If we look at these return types specifically for list, I'm returning a read-only list of T here. I've already hit the database, I've already gotten all the data and I've already turned it into an in-memory list when I come back from my repository. And I do that everywhere. That's again, helping developers fall into the pit of success because they're never gonna accidentally not know whether or not something coming back from that repository is gonna execute on the database or it's gonna execute in memory inside their application. If it came back from the repository, it's in memory. If it's huge and you just pulled back a million records from your customers table, then you just did that, right? You know that that's what you just did. And if you need to filter it, you know that you can filter it in a specification. The last reason why you should consider using specifications if you haven't used them before is that you get this catalog here of different types of queries that are very reusable and that have a specific name. So every developer can go and look in the list of specifications. And if this gets big, you know, you just break it out into folders based on whatever it is you're trying to query. But this becomes an alternative to having store procedures that would have all these different types of custom queries inside of your application. Now you can name them, you can version control them, you can stick them all in this one place and you can write tests against them, right? It's very easy for me to write tests that verify that these queries do what they're supposed to do. Let's see, that's most of what I wanted to cover. The other bit here is inside of the entities section. You'll notice these are broken up into aggregates. Aggregates are another domain driven design pattern I mentioned briefly where you fetch and store things as a unit. So when we grab the basket and its items, we do that as a single operation. You won't see us going and fetching out a basket item or a list of basket items directly without grabbing the whole basket. Same thing with an order, right? When we fetch an order in this pattern, we fetch the whole order with all of its items. And that's why we have specifications for things that say, hey, go get me the thing with all of its children so that when we make that call using entity framework, everything comes back at once like that. The other thing that we have support for is Docker. So if we come in here and we see Docker compose up, this will go ahead and create the application in Docker for us. It's running on Fort 5106, I believe. So if I come up here and hit localhost 5106, you'll see the application is running here in Docker. If I hit refresh or click on different things here, you can see that all this is logging out from the Docker container. So if you haven't used Docker before, you can grab this application. It's got all the files you need to kind of get started. Pretty simple, pretty easy to get going. I think that's everything I wanted to show and I'm almost hitting my time. So let me come back here, verify it on anything else. No, I'm not doing that. Here's some resources. Yeah, that'll be good to end with. All right, so I can check questions real quick and then I know they're gonna wanna move onto the next person and hopefully recover some time here. So yeah, let's see. Eric's Eflam's talking about my course on Pluralsight with Julie Lerman. That's this DDD Fundamentals course. You can check that out if you want. And let me see what other questions there might be before Javier goes and puts me back on video, which is fine. Wow, in 20 minutes. Anybody questions? There was a question earlier about if the demo uses CQRS. Does the demo use CQRS? No, it does not, but that's a good question. Let me talk real quick about the companion application for this. So there's also eShop on containers. All right, so if you wanna see advanced patterns, do a quick search for eShop on containers. There's another book that goes with this. This is a more advanced application. The book is here. Donut Microservices by Cesar and a couple others. And it's a great place for you to learn more about microservices, Docker containers, Helm charts, Kubernetes, Azure Kubernetes service, all that stuff. If you look at it, it actually is the same application, but broken up into microservices. So it's the same eShop. It's got a shopping cart, it's got login, it's got a catalog, but every one of those things is its own separate microservice doing the work. All right, there was another question. Do you have any complex business logic in view models? No, there should be no business logic in view models and certainly no complex business logic. All right, any other questions? And again, there was someone asking about learning, about DDD, where they share your course on that and any use of actors? No, it's not using actors currently. There's a few things that I want to add in the short term. One of them is more support for mediator, which I wanna show examples of. Another one is obviously we wanna do more with Blazor. I've been wanting to add SignalR for a little while and that would be easy to do alongside Blazor. So those are some of the easy three things I'd like to add along with updating of course to .NET Core 3.0. All right, awesome. So thank you so much, Steve, for the great talk. People were excited to join and I think the feedback shows that it was really good. So thank you so much for joining us today and sharing all of this. Yeah, is there a repo where Slides will be available for .NET Conf? I see some people asking and I'm happy to send them. Yeah, I was just asking here while we were on the break and there seems to be a repo, so we can share that later. So, but if not, you can share it on Twitter, on your Twitter account and then people can grab it from there as well. All right, sounds good. Yeah, so folks look for me on twitter.com slash rdallas. I don't know if I'm still sharing my screen, but that's me and if you have any questions, hit me up there or find me on Twitch as rdallas. Thanks. Yeah, thank you.