>> Now we are onto
Steve Smith talking about clean architecture with
the ASP.Net. Here we go. >> Hey, thank you very
much. Welcome everybody. My name is Steve Smith and today
we're going to be talking about clean architecture in
ASP.Net Core 7 apps. I work for a company called NimblePros we're
small.NET consultancy, help a lot of companies
moved to.Net Core,.NET 7 and implement architecture and
microservices, things like that. I use clean architecture
quite a bit. I have a template to help others, including my clients,
get started with it. When they're creating
a new microservice or creating a new ASP.Net Core 7 app. They can just start from
a solution template and it has all the pieces already
set up and ready to go. I'm going to talk
about a little bit of the theory behind
clean architecture. We're going to jump right
into the code because we only have about 28 minutes
and I have to go really fast. Let's start with what
is clean architecture? Clean architecture is the
architecture formerly known as onion
architecture or hexagonal, or sometimes ports and adapters. It's really meant to be a domain centric approach
to organizing dependencies. I have a course on plural
site about solid principles. If you follow all of
the solid principles, you cannot arrive at this
clean architecture style of arranging your dependencies
using dependency inversion, relying on dependency injection, focusing on interface
dependencies rather than concrete dependencies,
things like that. That's mostly what leads you
toward clean architecture, along with a healthy dose
of separation of concerns. The other thing about clean
architecture is that you want to depend on infrastructure
as loosely as possible. You're going to have coupling to your infrastructure
concerns, your database, how you send e-mails, how you call various web
APIs you might leverage. But you want to have those
things at arm's length. You don't want to
have to do surgery on your whole application just
because you change how you send e-mails or you want to
be able to test it with an in-memory database instead of an actual Cosmos DB or
something like that. Also, by eliminating or minimizing the direct dependency
on these infrastructure concerns, it makes it so that
we're better able to focus on the business logic or the domain logic if you're using
domain during design terminology. Now, clean architecture
is not meant to be a one size fits all solution. It's really designed for
certain types of applications. It's very closely aligned
with domain-driven design. If you're using
domain-driven design, cleaner architecture is often a good choice because
it does a good job of keeping your domain model very
pure and free from dependencies. It's going to be in its own project. That doesn't depend
on anything else, which is really nice. If you don't have
complex business logic and you're not trying
to write testable code, then clean architecture
might be overkill. You probably don't need this
for a quick demo application or even a lot of internal apps or things that
are just a lot of CRUD, create, read, update, delete types of applications
with a lot of logic. Clean architecture is useful when you expect to have
something you don't want to write unit tests for
and have confidence that the business logic
is working properly. If you don't have a lot of conditional
logic in your domain model, then cleaner architecture may not provided a whole lot of benefit and it maybe a little more cost in
terms of the number of projects, number of files involved than if you just had everything
in one web project for instance. You want to make sure too, that you're comfortable
with the fact that this architectural design is
enforcing some of these policies. You might have a general rule that you don't want your
domain model to depend on, say, a DbContext from
entity framework. Maybe somebody else on your team decides that would be really
useful at some point, they want to inject
one into a method. If you're just putting everything in one project, nothing prevents that. If you have new DbContext
is in one folder and your domain model is in another
folder all on the same project. You can just use whatever
you want, wherever you want. But when you use the clean
architecture solution, you are basically relying on the build system to enforce
some of these rules because the core project does not
depend on the project where are the DbContext is located or any
other data access concerns. There is no way for someone to take that dependency
inside the domain model. Visual Studio, VS Code, the compiler, they're all
stop you from doing that. That policy is actually enforced
by the architecture itself. Now if you don't need that, if you're a solo developer and you know that you're going
to do things the right way. When you want to bend the
rules or break the rules, you're cool with that and you're
going to do the right thing. Then again, you may not need
this type of policy enforcement, but you can think about the clean
architecture solution structure as being very similar to using a strongly type language or using scoping for your fields
inside your classes, having protected or
private visibility. You don't need to have any of those constraints inside
your code to write software. We could write all our software, which is static global
variables everywhere. But we use these scoping rules
and these constraints and these type systems to make it less likely that
we'll make mistakes. That's exactly what this
architecture does for you. Now, typically, there have been two approaches to
architectural layers. The two most popular, one of them is something
that looks like this, where you have this N-Tier,
N-Layer architecture, where each layer depends
on the layer below it and ultimately the data access layer
is the lowest level code layer. It depends on the database. What clean architecture does
is it flips this around a little bit so that the domain model is the
lowest level dependency. Everything else depends on it. The database is off to the side. It's not the key focus anymore. You can see here's an example
of N-Tier architecture. This is actually from 2001
from Microsoft MSDN website, it's showing guidance on how to
move from classic VB6 or ASP things where everything
was managed together in one file toward some more layering
and some more separation. Then here's an example
of what hexagonal or ports and adapters
architecture looks like, where the domain layer is the
center of the universe and it exposes these ports
or these interfaces, though then implemented by
adapters to do things like talk to other web services or communicate data to a database,
things like that. With our clean architecture
approach that we're going to use, we're going to have our
core project in the center, and that's going to have
our domain model as well as the abstractions that we are going
to use, different interfaces. Around the outside of that, we're going to have the web project, which is how applications are
going to interact with that. If you've got a blazer, or angular, or whatever type of front end needs to communicate with
this web project. That's going to be one of the external things that
depends on the core project. Then we're going to have also the
infrastructure project separate. It is responsible for all of the other things you're
going to collaborate with. Any types of web services
or Azure services, databases, especially
all those things are going to be inside of the
infrastructure project. Then if you have tests, which of course you should, those will be in separate
projects as well. Testing the core project
as well as other projects. I mentioned that there is a template that you can use to
go in and get started with this, you can grab it right
off of nuget.org. It's totally free. It's
really easy to install. If you go to nuget.org, you'll see the instructions
right here to just say, dotnet new- -install and then
specify the template and version. This one is for.Net 6,.Net 7 will be available in a short while as
soon as we get that published. Once everything is
out of prerelease. Once you have the
template installed, you can just say dotnet new and then clean-arch A-R-C-H to
create a new template. We'll see that in just a second. But first, let's review the rules. The rules for clean
architecture are first of all, that you're going to model, all the business rules
and all of your entities, the types that you're using, your customer or product
or things like that. Those are all going to live
inside the core project. Then the second rule is that
all of your dependencies flow toward the core
project, not away from it. Core doesn't depend on other things, especially
not infrastructure. Then lastly, we're going to say that the inner projects
are responsible for defining any interfaces that we need and an outer projects
will implement them. Sometimes the same project will implement interfaces that are in it. We'll see that in a minute. Let's start with the core project. Again, this is the
innermost project. This is the one that has all
your business logic in it. It's going to have
things like interfaces. It's going to have your
domain model aggregates if you're using those and entities, it'll have value objects, which are other domain model types
that don't have an identity. They wouldn't like
date time and string. You can compare those
based on their values, not based on an ID. In domain services, any custom domain exceptions you're going to have
with all live inside of core if you're using domain events
and there are event handlers, these frequently would
be in core as well. Specifications which
are really nice pattern to use for data access. I have a nuget package for that
as well. You should check out. Combining specification with
repositories for data access makes it so that you can have
really powerful data access on top of entity framework without
having to constantly write additional repository classes or add additional repository methods. If you've tried using repository
and found that part of it to be annoying that you can't say and add more classes or methods, checkout specification because
it totally solves that problem. You may have custom validators using fluid validation
or something similar. You may have enums or
smart enums as well. You could even have
custom guard clauses or other utilities that are inside this core project that represent the business
logic of your system. With that, let's do a quick demo of the core project as well as how
to get this template setup. I'm going to go ahead and just
bring up the Terminal here. >> I have not yet installed
the template yet, so I'm going to do that right
now in my Scratch folder. I've install the template
in my local machine. I haven't created a
new template for this. Then we'll cd into
DotNetConf.2022Eddition, and we can just take a look
at what that created for us. Now when you create things
with this template, the nice thing about it
compared to just downloading a zip file from GitHub is that
it removes everything for you. The fact that I named it DotNetConf.2022Eddition
means that that's what all my namespaces and
folders and things will be, so you don't have to do that tedious
work of renaming that stuff. We can obviously do this in code. Visual Studio Code
will work just fine. We'll bring this up
and we'll open it in Visual Studio as well
in just a moment. Inside of this, you'll see
we've got four folders. We have our core, our infrastructure and web, as well as something
called shared kernel, and we'll talk about
that in just a minute. Now, inside of here we're
going to take a look at core. Core has a couple of
top-level aggregates so you can organize the inside of
these projects however you want. But my recommendation is to, rather than organizing them by the type of thing it
is like entities, value objects, events, interfaces. I prefer to organize it based on the type of application
that I'm building. In this example, out of the
box you get a little bit of sample code just to show you how the patterns work and
how things fit together. You can totally delete
all that stuff. It's just part of the template, but it shows you how you
might organize things, and so within the
contributor aggregate, there is the contributor
entity here, and it's going to just be a class
that inherits from entity base. It has a few properties
and things on it, and it also has these
related events. Inside the events folder, there's this notion
of a customer deleted event that just takes in the ID
of the thing that was deleted, and so if you're
using domain events, it makes sense to put the
domain events that relate to a particular aggregate or to
a particular entity near it, rather than just
putting all of them in the one top-level events
folder, for example. We have interfaces for different services and
things we want to use. A lot of the interfaces we're
going to use are pretty common, and so they're actually
defined in the shared kernel, which we'll talk about soon, and then the project aggregate is
a more complicated object graph. It has a project at its root, and then each project has
a list of to-do items, and so to-do item is another entity that's also inside the
same aggregate folder, and it just has things that you
might have on a to-do list, like a title and whether or not it's done and who the contributor is. Inside of here you've
also gotten more events. You've also got some
handlers for those events and then some
specifications are defined. Now I don't have time
to really talk about how the whole specification
pattern works. But essentially each specification allows you to define a query in it, and that query can then
be translated at runtime using Entity Framework Core
or another RM so that you're able to capture the query logic
in your domain model and not have this query logic spread all over your controllers or endpoints
or wherever else they might be. That's pretty much a
quick overview of what you can do inside of
the core project. The last bit here is just some domain services that you might have. For instance, here's a service
for deleting contributors, and the reason why
that's a service is because it wants to
also publish an event. Rather than just calling your repository and
say delete this thing, we wanted to make sure that when
we get to delete this thing, we also are going to
publish an event, and in this case it's using
mediator for that purpose. Let's jump back to the slides
and move on to the next section, and that is going to be the
infrastructure project. Inside of our
infrastructure project, what are we going to have in there? We're going to have
anything that has to do with external dependencies as well as implementations of some of the abstractions
that we might have created. If you're using
domain-driven design, a very popular design pattern from
DDD is the repository pattern, and so the repository pattern
is not any framework. You'll hear people online say, well, we don't need to use
repository because MD framework implements repository. Well, that's true but
the whole value of the repository pattern is that there is an abstraction
that you own, that is part of your code, and so Entity Framework does support adding and removing
things from a DB set. But there is no interface
there that's yours, that's the frameworks abstraction, and so you're not following
the repository pattern by leveraging some
third-party framework, you're only following the pattern
if you have that abstraction. Your implementation
of your abstraction would live inside the infrastructure
as these repositories. You would also have your DbContext if you're using Entity
Framework would live here. If you're using Dapper or using some other tool for
doing data access, that implementation would
live in this project. If you're using caching, using decorators, which
I highly recommend. These would also live inside of infrastructure and would use
whatever caching technology, whether it's a memory cache or distributed cache on top of
your repository implementation, and caching isn't only
for repositories. The other thing you might be
using is third-party APIs, where maybe you're sending emails, maybe you're talking to GitHub API or Twitter's API
or whatever it might be. You might also want to add a caching decorator
around an API client. So keep that in mind when you're
creating these API clients. If you have stuff
that you're reading frequently and it doesn't
change too often, especially if you've got limitations on how frequently you
can make those requests, caching can really help
you here in the decorators the right pattern to use
for that most of the time. If you need to talk
to the file system or if you need to talk
to Cloud-based storage, all that stuff is infrastructure. If you need to send
emails or SMS or other messages, that's all infrastructure. You don't want to
have hard-coded into your domain logic or in your web UI. Anything that says, hey, I'm going to talk
to a system DotNet, DotMail and use SMTP
to send this email. Rather, you want to have an
interface for sending email, and one or more implementations of that interface would
live in infrastructure. Maybe when you're on localhost, you don't actually send emails
and you have some fake version. But when you're in the Cloud, you're going to use SendGrid or whatever it might be to
actually send emails, and it's really easy to
swap between those in your dependency
injection if you've got both those types sitting in
your infrastructure project. If your logic depends
on the system clock, it's often a good idea to
have an abstraction for this, an ID time or an iClock interface, and then inside of infrastructure, implement that and it's really
trivial to implement it because typically your interface is going
to have now or today on it, and you're just going to say, Well, here's a class that is the system clock date time that implements the ID time
interface, and by the way, it's now property is
just datetime.now, so it's trivial to implement, but it can make it much
easier for you to do some testing around the system clock that would otherwise
be very difficult. You may have other services in
here as well with dependencies, and you may have certain
interfaces that are needed for those services
that have dependencies. If you have a service
that works with Azure, let's say, and it's using Blob storage types or
something like that, and it's passing around as an
argument or as a return type, a custom Azure type
from an Azure SDK, the interface for that service should live inside
of infrastructure. Because if you put that
interface in your core project, now your core project would
have to have a reference to that Azure SDK in order to work, and we don't want to
have that dependency in core so we're going to keep those interfaces and their
services inside infrastructure. Now let's take a look at
the infrastructure project, and at this point, I'm
going to go ahead and create openness up a Visual Studio, so let me just open
project or solution, and we'll go find the
DotNetConf.2022Eddition. >> Now I didn't like that. I
think this is the preview build. Let me just check that real quick. This is 174 preview or
thought this would work. But note doesn't look like it likes
this project for some reason. I'm not sure what's going on here, but we'll just keep with code. Let me switch back to VS code
and we'll just continue on. Next thing we're looking
at is infrastructure. Inside of infrastructure you
can see we have things like an SMTP mail sender that knows
how to send e-mails using SMTP, the I e-mail sender is
defined inside of core, where as you can see we have
core interfaces up here. E-mail sender just has
this SendEmail async. Then here's the
implementation details for sending with our
local SMTP client. But if you wanted to
wire this up to use fake e-mail, you have
this one as well. It's not at all unusual when you're using clean architecture and you're using a dependency inverted
architecture like this one, that you would have more
than one implementation of an interface that you could swap between depending on which
environment you are in, what you have in your personal, like local development environment might be different than
what you're going to set up in a shared dev environment that you deploy two or staging environment
or production environment. By having these different
versions available, you can easily have the right dependencies running
in each environment as you go. In addition to that, you're going
to have any of your data needs. This is going to include
your dB Context. In this example, the
dB Context just has these three types of entities that come out of the
box with a template. You can delete all this, but it's just there to show
you how would you set up. You've got to do items,
projects, contributors. In here also is an
implementation of how we're going to send out domain
events after we save logic. You'll see this overridden Save
Changes async method here. Within this, this is where
we're going to check and see if any entities have
any events on them. If they do, we are going
to dispatch those events, in this case using mediator. That's how some of
these domain events occur when you save
something inside the system, it triggers something else
happening elsewhere in the system. To really nice pattern for decoupling logic inside
your application through the use of events. If you're already
familiar with events from userInterfaceStyle events like
click events or load events, it's the same idea but applied
to your business logic. Then our repository here, notice this is our type that we own and it's referencing
interfaces that we control. This is using the repository base in this case is coming from
our data specification. But I'm inheriting it and I can override or change anything I want. This is open source,
so I could totally forget it or change it and
do whatever I want with it. Even though it is a third
party library here, I have control over it. Then these interfaces
here are again, things that I have control
over and I can modify. Anything I want to do to change
how my repository behaves, or if I want to add additional
methods or whatever, I have that control to do it here. Now, because the specification one pretty much comes with
everything you generally need, most of the time for
most of my clients, we don't change this very much. But the important detail is that
you have that power to do it. You don't couple your
entire application everywhere to a framework or third-party library
implementation detail like a dB context that will make it more difficult for you to change
that decision in the future, to extend that behavior using
caching or other things, and to be able to modify it based
on what environment you're in. The other thing that you
would have in here are your any framework
configuration files. You can specify how
the types should be configured with EF and how
they map to the database. That's infrastructure. Now
let's look at the web project. The web project is your
standard ASP.Net core project. It's going to have anything,
it's an ASP.Net Core type. It's going to have
your API endpoints, whether you're using minimal
APIs or controllers, or an API endpoint package like
one of the ones I've published. If you're using
server-side rendering, you might have Razor pages, you might have
controllers and views. Any types of DTOs like
API models, ViewModels, if you're using the Reaper pattern, the request endpoint
response pattern, those requests and response details, would all live here using filters, model binders, model
validators, tag helpers, any of these ASP.Net Core
ideas or concepts or patterns, all of these things would live in the web project because that's
where ASP.Net Core lives. Now this also serves as your composition route
for the application. This is the application entry point. The composition is where your
dependencies are composed. Inside of program.cs, you're going to have the
service collection is going to wire up all the services
that you're going to use. It's going to specify
service lifetimes like transient are scoped, etc. It's going to happen in the
web project or it's going to be kicked off by the web project. You may have other services with their own interfaces here as well. Again, this just depends on
if those interfaces rely on ASP.Net Core types or types that
are defined in your web projects. If you have a service that returns back any API model or a ViewModel, we can't put that service in core or an infrastructure because
those DTOs are defined in web. We're going to put
those services and their interfaces inside web as well. Now let's do a quick demo.
Take a look at that. Inside of the Web project here, we've got API models,
different DTOs. We've got an API folder
that's using API controllers. The idea here is when you
install this template, you're probably only going
to use one type of thing. You're going to be doing APIs or you're going to be Razor
pages, you can do views. Depending on which flavor
of ASP.Net Core you want, your going to keep that and
delete everything else. If you'd like API controllers and that's how you
want to do things. There's an example of a
project's controller here. You would keep this folder and it's related folder and we
delete the rest of it. If you want to use
controllers and views, you can do the controllers folder
has things that return views. You keep that along
with the views folder. If you just want to
have API endpoints, you can do something like
this where you've got this create endpoint
and this is using the Dallas API endpoints project. It will basically create
one file per endpoint, which have really
nice way to do APIs. There's another one that is
using a different library, and this is the fast
endpoints library. This one uses the new
minimal API structure. It's actually a little bit more
performance than API endpoints, but has a slightly different
way of working with it. You configure it in
a configure method, and then you still have
a handle async method. It's basically using the same
Reaper pattern as API endpoints. If you have custom filters
they'd go in here, you've custom razor pages, V-models, reviews, all
that stuff lives inside the web project. We're
just about out of time. Let me cover one last
thing and that is, what do you do with
the code that you want to have in every one
of your solutions. That is what DDD calls
a shared kernel. It's a Domain Driven
Design term for holding these common types and typically
it's referenced by core. These types could be
used by anything. The best way to distribute
this is as a nuget package. Inside of the template I have
a project for shared kernel, but you should take
that and publish it as your own nuget package from your own internal
corporate nuget server. What goes in here, there's
going to be all base types, especially DDD base types. Your base entity, your
base value-added, your base domain events
or specification. Other common interfaces,
common exceptions, off types of things like, user types or things like that. Coming Guard clauses, other common
libraries you might use for DI. If everywhere you're using DI, you want to use artifact, let's say then that
might be inside here. If you have certain logging library
you want to use everywhere, certain validators, all that stuff
could live in shared kernel. However, you don't want to have
any infrastructure dependencies. You just want to make
sure that you don't keep infrastructure inside
shared kernel at all. It's even more important
than core because all the different solutions
that rely on this package, all of their core projects
are going to rely on this. It's even more important than
a core project that you don't include infrastructure dependencies
in your shared kernel. Let's take a look at what's insured kernel here that
comes with the template. Again, you would put this
in your own nuget package, but it just has base classes. Domain event base and
the implementation of the dispatcher for
sending domain events that in this case uses mediator. But you can swap that out with
something else if you prefer a base entity class that in
this case uses integers, but you could modify
it to be an entity of T so that you could have
integer or good support or string or whatever
you want to use for your key type and implementation
of value objects here as well. You've got those things
in here as well as some basic repositories
for aggregate roots, repository and domain
admin dispatching. With that, I am out of time. Some resources here
for you real quick. You can get the clean
architecture template from nuget or you can see the source on GitHub under our
Dallas clean architecture, you can also check out the eShop on web reference application that's
available from Microsoft. It's also on GitHub. It's also
using clean architecture. We'll show you how a real
application not built analyzing it. There's a companion even
if it goes with it, which is linked to right here. It's also find it from that repo. With that, thank you very
much and hope you're having a great.NET 2022 and I will
pass it back to you.