Clean Architecture with ASP.NET Core 2.2 - Jason Taylor

Clean Architecture with ASP.NET Core 2.2 – Jason Taylor



it's 11:40 I guess we can get started hello and welcome to clean architecture with ASP net cord 2 – I'm Jason Taylor and I'm an SSW solution architect you can find me on Twitter at Jason G tau or on my blog Korean flow net I try to blog once a week so the contents nice and fresh a last blog at NDC Sydney 2018 in September so not quite reaching that but I'm going to improve someone from my companies came to me last week and said would it be ok if you blog once a month and I said yeah that's that's fine I try to blog once a week so I think I can achieve once a month I've been a developer now for 19 years and I've learned the most important principle is kiss or keep it simple stupid and this principle states that systems should be kept simple rather than made more complicated and today I'm going to show you the simplest approach to building enterprise applications with clean architecture and this is the simplest approach because what I build is not simple or trivial I build medium to large to complex applications so you could definitely do things differently in something that's simple and trivial but that's not what this is about this is about enterprise application development let's get started with clean architecture the domain and the application layer are at the center of the design this is known as the core of the application now in the domain we have enterprise logic and types and in the application layer we have business logic and types the difference being that the enterprise logic could be shared across multiple systems whereas the business logic will exist only in this system now rather than have core depending on concerns such as data access and persistence we invert these dependencies so infrastructure persistence and presentation all depend on core this is achieved by adding abstractions or interfaces inside of core which are then implemented by layers outside of core a good example is the repository pattern so if we wanted to implement the repository pattern in this design then an irepository inside of core which is implemented then maybe as EF core or Mongo in the persistence layer now with with this design all dependencies flow inwards and core has no dependencies so infrastructure of persistence and presentation depend on core but not on one another and this results in an architecture and design that's independent of frameworks core doesn't need any frameworks to exist it's testable we can test the logic in that inside of core without a UI without a database and without a webserver and it's easy to test there aren't any external dependencies and we know the logics the most important part of our application so this is very important it's independent of the UI we'll have the flexibility to change the UI easily and that's a good thing right now we're dealing with a lot of different web frameworks angular react view we want to move to the latest and greatest and we'll be moving to Blaser so we want that flexibility when we do move to a new presentation technology we're not going to impact the logic inside of core it's independent of that it's independent at a data base so right now we might be using sequel server oracle Postgres but soon we'll be switching into cosmos DB it's gonna be a lot cooler when we're building blazer applications on cosmos DB and finally it's independent of anything external core simply doesn't know anything about the outside world now with this design you can see that there's only three layers now you might need more or you might need less it just depends on the complexity of your application the only thing you need to keep in mind is those dependencies need to continue to point inwards that's what gives us the great flexibility and the high maintainability and that's going to be the difference between an application that lasts a few years to an application that's going to last 20 years so this is my example Northwind traders show of hands who's heard of Northwind traders pretty much everyone Northwind traders is cool right no one no one thinks north and traders is cool I might change your mind so it's now cross-platform I have upgraded it it runs on Windows Mac and Linux so that's good it's dotnet core too too soon oh I played it two three EF core two to code first and data seeded that's cool right Northwind traders is cool again let's take a look so this is my Northwind traders solution you can find it on github I'm gonna share a link at the end so you don't need to take note of this but I just wanted to point out when you go to the repo there's some nice instructions here on how to get started and what you'll need and we can follow along with these instructions just to get it running I don't want to show you one thing initially just how to launch the application because there are two parts to it this has an angular front-end and so to launch the angular front-end we use we go to the command prompt Northwind web UI client up and we just have to run NPM start and that'll launch the front-end now from the backend we can just go to the web UI project and we can just press ctrl f5 and that'll launch at the backend if you launch the backend without the front end running as it is now you'll see this exception don't be alarmed sometimes my laptop just needs a little bit more time to get started so there we go it's running and if we come here and launch that's all good so this is a sample application it does have some functionality but its purpose is primarily to demonstrate the principles of clean architecture and the key points that I'd like to make in this presentation it has a nice open API behind it so you can explore the API and see some of the functionality and we'll talk about that a little bit later but I just wanted to give you a tour of of the project so I'll zoom in here for you here we go so you can see the solutions broken up into a number of folders correlating to the folders in the architecture and design I showed you earlier there's a docs folder here and that contains a readme that's the github readme so you can take a look at that and then there's the slides from the presentation so when you do go and have a look at this you'll have all of the resources from today now you can see here I have my core layers application and domain and inside each of those they're read miss also so you can get an understanding at each layer what should be contained within there I have my infrastructure layer which contains infrastructure and persistence I didn't mention this earlier but persistence really is a kind of infrastructure we just separated out because we've got a few concerns in there that we're going to encapsulate and so you could separate out other infrastructure concerns in this in the same way so if you wanted to have Northwind security for your authentication authorization concerns that will be an appropriate choice I have a Northwind common project which is for my cross-cutting concerns and it has no dependencies any of those other projects can depend on common it's a good place for things like string extensions or date/time extensions whatever whatever type of thing you might want to put in there and then you can see I've got a bunch of tests on just just to keep it all running nice and healthily okay skip through that so key points for this first section the domain contains the enterprise-wide logic and types and that's the logic that we can use in multiple systems the application contains the business logic in types and that's the logic that's specific to this application infrastructure including persistence includes all external concerns and the presentation and infrastructure depend only on application not on each other so infrastructure and presentation components can be replaced with minimal effort and that's because we're not going to affect call and we do that it has no external concerns so now we're going to have a look at the domain layer followed by every other layer in application and essentially in this talk I'll make some key points I'll highlight the things that I think are important so that you get a good understanding of the project but you'll also want to dive in and take a look for yourself because there's a lot more going on then I can include in the time allowed in this talk so inside of the domain layer we have entities value objects enumerations logic and exceptions let's take a look so the first key point that I'd like to make is regarding the use of data annotations so you can see what I have here is an entity called customer and it's nice and clean but if I look at the history we can actually find a version that includes data annotations here we go so this is another version but I'll make it I'll make that much bigger for you let's go 150 there we go so this version contains data annotations and and in the past earlier versions of EF we used to use data annotations all the time and they had two purposes and that was to instruct the ORM how to create the relational model and also to provide validation but in newer versions of the F core it doesn't do the validation anymore you can use dart annotations for validation it's just not something that you have courses are for and it also it does still use it for relational modeling but there's a much better way to do that we can we can actually use fluent API configuration instead and that means that we can keep our entities nice and clean and looking like this because really those sort of concerns don't belong in our domain layer anyway we want to keep that into the infrastructure layer specifically in the persistence project now the next point that I'd like to make we can we can use this example again is to initialize all collections and also to make the private the set as private or to remove them all together and you might think well why will I do that and it it's just because it makes life easier if we do that we don't need to think when we're accessing a new customer object we don't need to say hmm I wonder if the order collection has been initialized all right an if statement if customers orders equals null then initialize the orders collection we don't have to do that anymore and we gain control of the object it's it's about helping your developers in ensuring that they fall into the pit of success we make it easy for them to do the right thing and hard for them to do the wrong thing so the next one that I want to talk about is when you're designing your system let me just see I want you to think about this this entity we often use primitive types where where we shouldn't be using primitive types so here we have an ad account and its type is string and that's fine ad accounts are strings but not all strings are valid ad accounts the certain rules in the way that ad accounts will be created the value and there's certain ways in which we'll use the ad account for example sometimes we'll want just the domain name and sometimes we'll want just the username and sometimes we'll want to roll together in some kind of display format now that's logic there's validation and logic associated with using this ad account now if we don't find somewhere to encapsulate this that's going to be problematic that in a large system that logic is going to appear time and time again because new developers experience developers may not know about it so we need a good way to encapsulate that logic in a complex type and that's where value objects can really help so you can see here I've changed it to a value object and let's take a look at the implementation actually let's take a look at the tests okay so I have this ad account and it should have the correct domain and name so I can construct it using this factory method and all I have to do is pass in the ad account string and then I'll have access to the domain and the name so that's nice and easy I won't have to write logic around that I've got a method to string which I've overridden and it returns the correct format so we can see that if I construct a new object it's going to return the format which will essentially be SSW slash jason the original format i have an implicit conversion operator so i can take an ad account type and implicitly convert it to a string which is great for whatever purposes i need in the string format logging is one that comes to mind i have an explicit conversion operator so i can directly cast from a string to an ad account and I control the creation of the ad object if the ad account is not valid the ad account string it doesn't contain a slash then that's going to fail and and it brings me to my next point which is to use custom domain exceptions this exception ad account invalid exception will be a lot easier to debug than the index out of range exception that would otherwise be throw on I have a quick look at the implementation so you can see here it has a private constructor I'm controlling the construction of this object through the factory method it essentially splits the string into two parts and this is where it could throw an index out of range exception so I catch any exceptions and just say hey there's something wrong with this ad account so that's much better I have this type it encapsulates all the logic associated with it and I don't have to worry about logic appearing elsewhere when my developers use this type they're going to fall into the pit of success again they're not going to think about how do I get just the domain part how do I think get just the name part so when you're building your entities just think about this property that I'm creating is it really a string or is it more complex than that will I have logic associated with it is it a primitive type or is it a complex type okay so key points for the domain layer avoid using data annotations there are better ways they clutter up our domain and we'll see that soon the better approach use value objects where appropriate I mentioned before just think about it is it really a primitive type or is it more complex initialize all collections and privates and use private setters help your developers to fall into the pit of success and create custom domain exceptions so much easier to debug than an index out of range exception so now we're going to look at the application layer the application layer contains interfaces models view models and DT OS logic commands and queries we'll talk about that soon and validators and again custom exceptions so CQRS we all know by now CQRS stands for command query responsibility segregation and with CQRS we separate our reads from our rights and the benefits include maximizing performance and scalability and that's awesome but for me the most important benefit is simplicity i find that when i use CQRS everything becomes easier it's easy to add new features because I can just add a new query or command and it's easy to maintain our changes should be nicely isolated to a single query or command and so we're less likely to introduce bugs now if you like CQRS then you'll love using CQRS with mediator they like the perfect couple so with mediator we don't define our commands and queries as requests so the application layer just becomes a series of requests response objects and with that we get the fantastic ability to introduce additional behavior so we can add functionality before and/or after each request so behavior such as cross-cutting concerns such as logging cache invalidation and security become easier because we're using mediator we get this really nice pipeline which is powerful so let's take a look okay so you can see here in their application layer I've separated things by feature so everything related to customers is in this customers folder and there's only two things there's commands and queries if we expand those it's really obvious what everything in those folders will do we know that if we go into here it will be a create customer command and if we come into here that will be where we can work with our get customers list feature and that's a query let's take a look at this one inside of this folder I have everything that I need to work on this feature I don't have a separate folder for my view models I don't have a separate folder for my DTO so I don't have a separate folder for my handlers so when I'm ready to change this feature it's all here I don't have to jump around to find what I'm looking for now with mediator we define our commands enquiries as requests so you can see in this case this is a DTO for getting a list of customers at the moment it doesn't have any properties but I might start to add some so I might say things like page size filtering abilities what page that I'm currently on but with with the mediator framework we define these as type I requests and then the return type so this query will return our customers list view model if we jump in here and look at the handler we can see it's quite simple we're injecting an eye Northwind dbcontext and a mapper for our mapper and we're simply constructing a new customers list view model and this is this is a very nice approach because what's contained within here are only the things needed to return this query we don't have a large service that's responsible for many things which becomes hard to navigate it's a single file with a single purpose you can see the create customer command is quite simple also so this is our DTO it's a requester as well it doesn't return anything so we're not specifying that here and it contains everything that we need to go ahead and create a customer now I've taken a slightly different approach here I've actually nested the handler inside the command and that can help to improve discoverability we'll talk about that a little bit later you can see again it's quite straightforward essentially I'm creating a new customer entity I'm adding that saving the changes and publishing a customer created event and then with mediator I can create handlers that have specific functionality for that event as well the next thing I want to talk about is validation so we mentioned that data annotations are not so good at the domain level well I think they're not so good at this level also data annotations are ok we can use them for simple validation scenarios but not so much for complex validation scenarios for that I prefer to use fluent validation and let me let me show you an example so here I have an update customer command and it just has the DTO to an updater customer and then I have an update customer command validator so with the fluent validation I'm able to define the validation rules using a fluent syntax in a separate file and these rules are obviously simple there are there no more complicated than what you can support with data annotations but down here I have a couple of example more complex rules so you can see here I have a rule for the postal code when the country is equal to Australia then the post code should match this regular expression essentially it should be four digits and if not it will return this error Australian post codes have four digits so that's not much more complicated but it is a nice syntax we can do that with data patience but we also have this one we have a rule for the foreign it must have a Queensland landline when the country is equal to Australia and the postcode starts with four so in this case we're actually looking at Crossfield validation and we can do a lot with with fluent validation in this way really we take full control of the validation scenarios and so the implementation is up to us yes question I'm not validating domain entities I'm validating other commands or queries the requests to the application so this is kind of the first line of defense because everything's coming in as a request so what was that no no it's in it's in the application layer I like to have the things that change together together and so the the command and the command validator sit together so that when I need to update that feature it's easy to access those things I prefer not to have to jump around okay so one of the other things about using mediator I mentioned as we get that pipeline and so to run these validators I have what's called a pipeline behavior and if we look at this this is a request validation behavior so remember everything that's coming into this application is a request and I have a pipeline behavior which handles these requests so the requests might be of type create customer command or get customer detail query if there are validators associated with those commands or queries this little process here will run them and if there are exceptions it will return a validation exception so that's a custom exception that was thrown from the application layer so that means the request validation is automatic the developers don't need to think about that if they associate a validator with her Qwest then it will be validated and that's great that's a part of the mediators our pipeline so here we have another simple behavior this is an I request preprocessor so it happens before the request runs in mediator and it's quite simple essentially we get a request let's say create customer command I take the name of that request and I just log it using dotnet cause built in logging abstraction so I just say Northwind request create customer command and then I take the actual request DT Oh everything that's in the create customer command and I serialize that so that's going to be available in the log and then finally I have another one which is just a simple request performance behavior and so this one is a pipelined behavior like the validator so with this one I start a timer I complete the complete the request get the response and stop the timer and if the request happens to take greater than 500 milliseconds then I log a warning to the database in much the same way as I was logging the information so that's kind of that's kind of great any request that comes into the system with a few lines of code I can log that request I can I can when I add the user details I'll be able to filter it by user set the date range and I can see kind of what they were doing or where they had the problem and what kind of exceptions were associated with that and because the request itself has been serialized I can take that serialize request and I can add that to a test that's probably going to fail because will the customers complaining and then I can fix it and the test will pass so that's pretty cool all right and the other thing that I wanted to talk about was abstractions so in this in this layer we have a couple of abstractions so I have the I Northwind dbcontext which is my interface instead of using the dbcontext directly now this is a compromise I've chosen to use entity framework core directly because make no mistake even though this is an interface I still need a dependency on entity framework core in this application now if I wanted to avoid that if I had a good reason to use the repository and unit of work then I would actually implement those patterns and I wouldn't need the dependency on energy framework or but these are some of the real decisions we make when we build software today we know the right thing to do but we make choices based on speed and cost and quality the other interface that I have in here is a demonstrator for I notification service and the important thing to note is it doesn't matter where the implementation for the notification server is services it's going to be an infrastructure somewhere but core only depends on this interface and this message type that's not coming from infrastructure that's coming from core as well so I'm sure that if we use a service like sand grid or something else it's going to have our own types and we could easily take a dependency on that and that would save us time but that means that we will be tightly coupled to that framework and when we want to change to a new messaging service we'll have to rewrite those types so just to avoid that straight away make sure that the interfaces within core make sure that the types are worth in core and do the mapping necessary okay key points using CQRS and mediator simplifies your overall design fluent validation is useful for all validation scenarios mediator simplifies cross-cutting concerns we get that really great pipeline so we can implement all sorts of things there and it's independent of infrastructure and data access concerns all right now we look at the persistence layer so inside of the business in layer we have our DB context because this solutions using energy framework or we have migrations configurations seeding abstractions so here's the big question used to be a bigger question should we implement these patterns and I'm going to answer for you because every time I ask the question most people say no now there'll be one or two people who will say yes but the fact of the matter is with architecture and design it always depends now in the general case it's not always the best choice because you've core insulates your code from database changes EF court is an abstraction and we choose a database provider to work with that abstraction we can choose sequel server we can choose Postgres now we can choose cosmos DB DB context acts as a unit of work it's implementing that pattern and the DB set axes are repository so a common reason for implementing these patterns with earlier versions of EF and with other ORM S is for unit testing but now if core has features for unit testing without repositories in the way of the EF core in-memory provider so that's what I think but what do the experts think so Jimmy Bogart who was at the conference this week he's the chief architect at head spring and the creator of auto mapper and mediator which were using in this solution he says that I'm over repositories and definitely over abstracting your data layer so I'd say I'd say he's against implementing repositories and unit of work in in in most scenarios then we have Steve Smith Microsoft MVP and Regional Director for almost ten years and he says no you don't need a repository but there are many benefits and you should consider it now I think Steve has been very diplomatic here I think that if you came to him and showed him your solution and you didn't have a repository he'd show you why you need it so very diplomatic he says you don't need it but I think he's kind of on the side of implementing a repository and next we have John Smith the author of energy framework core in action and he says no the unit of the repository slash unit of work pattern isn't useful with energy framework core so even the experts don't agree and what does that tell us well it's simple as is the case with all design patterns we need to think about the problem that they're trying to solve if if the pattern if applying the pattern solves that problem for us then go and head and implement it if you implement it without a problem to solve then it just introduces unnecessary complexity so same approach or design patterns okay let's have a look at the persistence layer okay so first the point that I'd like to make is the precision Slayer is independent of the database insofar as there is a provider that we can change to with energy framework core you can see in this particular application the provider I'm using a sequel server so I could change that to Postgres so in that way it's independent of the database I have changed this solution to Postgres in the past and and run it on Linux and it works quite well we talked about not using data annotations in the domain layer so how do we configure the ORM so that so that it knows how to map from the object model to the relational model we use fluent API configurations so in the fluent API configuration for customer we define everything that we need and it's outside of the domain layer so nice and clean in a separate file we also can automatically apply these configurations in our DB contacts we just need to add this line so model builder don't apply configurations from assembly and we just say which which assembly the configurations are contained within so this is a new feature I think it came in in in – – before that we had to either do it manually or write our own code to apply it automatically now the most important thing with working with any framework which is conventions base is to understand the conventions because that's going to make us more productive so you can see here I'm configuring the employee entity but right after about any of you who know entity framework well well know that I don't need this line by convention it will recognize that an employee entity with an employee ID or an ID just by itself will be flagged as energy front by energy framework as being a key so when you're working with a conventions based framework it's important to first understand the conventions because that's going to make it easier for you to to work with that system you'll be more productive and you co will be simpler because of it I can just remove anything like that so I want to just touch one more time on the repository pattern so obviously in this application I've chosen not to implement it but we should definitely talk about a good reason as to why we might implement the repository pattern so in a complex design we might like to implement the repository pattern at an aggregate route so an example in this application would be an order with order details and why would we do that well we would implement the repository at the order level and not at the order detail level and in this way we will control how orders can be created updated and deleted we'll make it easy to encapsulate that logic so that our developers can't go directly to the order detail where we haven't written the correct logic around adding updating and deleting order details directly so remember with the repository with design patterns first figure out what the problem is that you're trying to solve and then apply the appropriate pattern okay so key points for the persistence layer it's independent of the database using EF call we can switch providers we use fluent API configuration over data annotations it's simpler to to wire up and it keeps our domain free of extra attributes we prefer conventions over configuration and when we're working with a framework that is convention space we need to understand those conventions clearly and we should automatically apply all entity type configurations we can do it with one line of code so that way we won't forget okay next the infrastructure layer so within the infrastructure it's essentially our implementations persistence API clients file systems email / SMS services the system clock kind of an important one anything external so let's have a look in this demonstration I've kept the infrastructure layer quite simple I've got my machine date/time which basically has a couple of properties it just has now in the current year any anyone think why we might want to actually implement machine date/time and only have core depend on the interface I'd a time rather than using date/time directly so what's that yeah possibly yep testing yep testing testing is the the main reason that comes to mind for me so if we have some logic that we need to test and perhaps that logic is only can be tested on a Tuesday well if we're using date/time now directly well we can only test that on a Tuesday maybe we can write some logic in our test to check that it's a Tuesday and only run but with an i date/time interface we can we can mock that out and make sure that it's a Tuesday another nice advantage that I came across is that I was able to switch this out for an implementation where the end user was able to supply the day time for the system and this was used for testers so they could actually configure the date/time to test things that were time and date based so they could advance the system clock bring her back to the current date time bring it back in time that was quite useful too so we have a machine deck time implementation there and then I have my ultra fast notification service responsible for sending notifications so we saw the I notification service implementation in core and this is the side of the interface in core and this is my implementation it's superfast but the messages don't go anywhere okay so no a point an important point to make about the infrastructure layer is that no layers should depend on infrastructure infrastructure depends on core but we shouldn't have for example presentation depending on infrastructure we might have something in presentation that wants to send a message for example but when we do that were actually forced to write logic in presentation logic to orchestrate the communications between presentation and infrastructure in order to send a message now that's logic that we can't reuse we want to avoid that we want to push that logic into core so that when we change out that presentation layer we don't have to move that logic as well so it's an important point all of the logic that we have for this application should be carefully encapsulated within core okay so key points so the infrastructure layer contains classes for accessing external resources such as file systems web services SMTP and so on it implements abstractions and interfaces defined within the application layer and no layers should depend on infrastructure so next the presentation layer so what's in our presentation layer it's the it's our clients it's the spa whether we're using angular reactive view could be just a Web API razor pages MVC or even web forms let's take a look so what I want to show you here is something that helps tie all of this together we're going to take a look at the controller's so I have a products controller and I want to show you a typical example so this is how controllers were typically built we would have a products controller we would inject the dbcontext directly we would return entities from the controller and we would accept entities in order to to update persistence we would have lots of logic in here and generally it would get hard to work with there's a couple of problems with this the first one is that the the DB context the persistence is one of the lowest levels of our system and the controller itself is one of the highest levels of our system so when we bring those two things together we only have one place to write our logic and that's in the controller that's in the actions so that's not great and as you know there's lots of problems with returning entities from controllers and using them outside of this area for example if we have a product and it has a collection of categories and that relinquishes let's say we're updating a customer and we're not supposed to update the customers email address but then someone over posts and supplies the email address anyway even though it wasn't on the form well now they've changed the email address to their email address they can reset the customers password take over the account so there's lots of issues involved in doing that so we can control that much better let's have a look at a newer version so in this version i started using CQRS and with with CQRS in this implementation I wasn't using mediator I just added all of the interfaces to the commands increase that I needed now this was a lot better I have a lot of kind of boilerplate here in order to get dependency injection to wire all that up for me but when we look at the actions themselves they're now really simple they're just one or two lines of code no longer working with the DB context no longer encapsulating any logic so even though is it is it is a great step in the right direction the logic is now in its respective command or query so it's quite simple so minor update here we started using from services so this is a nice little attribute with the dotnet core dependency injection we're basically saying to dependency injection to inject this query for us so we don't have to add all of those commands and queries so it'll just inject it and we query dot execute inject the command and we can execute that nice and simple ok and this is where we introduce mediator so with mediator all we need to do is inject a mediator and it knows where to find the the handlers that are associated with a command or query so then we can just say mediator send this get all products query or mediated send create product command so it's nice and easy but now of course we have the power of the mediator pipeline so we can do a lot more with the requests and the responses that are moving through our application I think I'll go to the latest version now so not too much has changed since we introduced mediator I've I have a bass controller where we're using property injection to get mediators so I don't need to add anything to the constructor I can just inherit from this base controller but you can see it is really simple this controller doesn't contain any logic it's it's basically become infrastructure it just has a single responsibility for products to turn a products request into a products response that's it all of the logic is elsewhere if we have to switch away from web api to something like Nancy it's not really going to be that big a deal one of the things that I'd like to show you in the presentation layer is I also have a filter so when we run validation in the application layer we throw an exception if there are validation errors a validation exception and it's up to the client how they handle that exception obviously a console applications going to handle it differently to a web application in this application I've created a simple custom exception filter and it essentially for any exceptions that happen it'll catch it turn it into a bad request and return the failures back to the client and that's going to be in a format similar to model state validation so one point that I want to make here is that we should work hard to return well-defined view models and what do I mean by that well we could return a product with the product details in order to edit a product but we could actually return much more information than that we don't want our client to have to make additional requests so for example when we're turning in a product view model we can also return things like edit enabled or delete enabled so the clients not forced to make authorization checks to get the additional information that it needs we can encapsulate within the view model everything that the view needs so that it's simple for new clients to use that view model and it's also simple for us to test we can write a test to see that the edit button is disabled by testing the view model directly and that's something that's inside of core and as we know core has no external dependencies so it's very easy to write tests for core so by encapsulating those things in the view model we do get great flexibility in in changing our our presentation layer in the future the last point that I want to make in this in this presentation layer is that we should use open API with our Web API s so you can see here I've got an open API UI so this is the swagger UI and for my developers they can come and explore this this API they can learn about the API and understand how it works so I've got a customers get all customers here I can click try it out execute that and I can I can kind of see the response type and that sort of thing but I can also generate code from that because I have the API specification set up I've actually used it to generate an angular client for this application so if I dive into the angular application I have this Northwind traders API written in typescript so this is this this is quite big I didn't have to write any of this and I can use this directly so by implementing open API on on the Web API we're actually making it easier for our developers to work with the system and we'll make it easier to to build front-end applications in a microservices architecture I can also generate c-sharp clients so it can make it easy to communicate with that with that microservice if that's my preferred method this application is configured to use a tool called n swag to generate the open API specification and it actually happens automatically when I build the web UI it will build a new specification and place it here in WWE route specification JSON and it will also generate the angular client that you saw and that's actually using msbuild so n swag has a nice msbuild package and if we scroll down in this CS proj I've got to comment it out at the moment don't need to it actually just runs n swag after build and we'll keep that up to date so I don't have to worry about it anymore usually with these tools you have two choices you can do it when that web applications running and automatically or you can do it offline kind of how I'm doing it now I find it preferable to do it offline because then I don't run into any kind of issues relating to authentication or forgetting to start the web front-end when I'm trying to generate an angular client and that sort of thing okay key points for the presentation layer so controllers should not contain any application logic we should encapsulate that within our commands in queries we should create and consume while defined view models we want the view model to contain everything that the view needs and open API bridges the gap between the front end and the back end I have some recommended resources so first is the book architect in modern web applications with asp net kora and Microsoft Azure so this is by Steve Smith definitely encourage reading this don't worry if your stack is in asia or asp.net core there's a lot of great value in this book this was the book that I read that first got me interested in clean architecture I think it's actually up to version 2 to now next book is building micro services so dotnet micro services architecture for containerize dotnet applications don't worry that it says micro services don't worry that it says Asia and and don't even worry that it says containers fact of the matter is there's a lot of great value in this book even if you're not building Micra services architecture there's a lot to learn so definitely have a read of this book the value object that I implemented it actually has an abstract base class behind it and that's where I got that implementation so great book and finally couldn't do a clean architecture presentation without referring to Uncle Bob's book on clean architecture so obviously it's a one of one of the best resources so next steps thank you for coming to my talk today if you're keen to learn more please grab the code in slides you can find it at bitly Northwind traders and get started try it for yourself I think you'll find that this approach is simple to build and maintain all the way from development to production thank you [Applause]


4 thoughts on “Clean Architecture with ASP.NET Core 2.2 – Jason Taylor

  1. I have this question in regards to using value objects:
    I have double LONGITUDE and double LATITUDE in PLACE entity, the presenter as well as many of the ddd ecosystem, would recommend encapsulating LONGITUDE and LATITUDE within a value object named gpsLocation for example. the question is:
    when it comes to persistence, how would entity framework understand how to persist gpsLocation, which is just another property in the PLACE entity?!

  2. This layout with a window of the presenter in the lower left taking up 1/4 of the screen is pointless. Even on an 85inch screen the presenter is too small to be recognized and no one is really looking at that lower window. These videos would be so much better if the entire screen was used for the source code window, IMO.

Leave a Reply

Your email address will not be published. Required fields are marked *