I have started my career working on 3-tier architecture. Gradually, it’s shifted to N-tier architecture. In this article we will try to learn clean architecture with simple explanation compared to 3-tier architecture. Let’s see how Clean Architecture (Uncle Bob’s style) relates to 3-tier architecture.
The following diagram represents simple 3-tier architecture.
In the 3-tier architecture:
- UI/Presentation Layer contains
- Business Logic (BL) Layer contains
- Application logic
- Business logic
- Integration logic (with 3rd party services/interfaces)
- Methods that call data layer
- Data Layer contains
- Data Models
- DB access logic
Here each layer is communicating with only the next layer. So, the UI layer can’t communicate to the DB layer directly and it has to go via BL layer and vice-versa. And, the User can’t talk to the BL layer directly but the flow has to pass via UI layer.
The result is more mapping code, which further amplifies when more layers are added.
As time went on, more and more layers added with the following purpose in mind.
- Make the testing of your code easy
- Decouple the architecture from the underlying framework using which you implement different layers of your architecture
- Separation of Concerns – divide layers responsibilities using SoC
- Make the architecture less dependent on (or affected by) future changes in UI layer or DB layer
Clean architecture has many other names like:
- Onion architecture
- Hexagonal architecture
- Uncle Bob’s architecture
- Screaming architecture
- Domain centric architecture or Domain Driven Design (DDD)
- Vertical slice architecture
Clean architecture is not something completely new or groundbreaking, but it just tweaks N-tier architecture in a way to make code more testable, maintainable, adaptable to change, and long lasting.
The following diagram is a simple representation of clean architecture.
In clean architecture, dependency flows inwards. So, the Domain layers is independent totally. The application layer depends on the Domain layer. Presentation and Infrastructure layer depends on the Application layer.
Two core components of the models are:
- Application layer – depends on Domain layer
- Domain layer – depends on nothing
So, you can easily use domain layer independent of other layers in other projects or use cases.
Fundamental idea is core components (Domain layer and Application layer) should not dependent on infrastructure and data access related concerns so that dependencies are inverted.
This follows the Dependency Inversion principle, meaning dependencies are injected instead of being created. (Also called Hollywood principle, “don’t call us, we will call you”) So, for example, if an application wants to access a database (infrastructure layer) then the application will define an interface and infrastructure layer will implement that interface.
This will give the following major advantages:
- Decouple core layers (Domain layer and Application layer) from Infrastructure layer and Presentation layer
- Persistent ignorance (data can be stored in DB, or passed to the Web Service, Web API, etc. but Application layer don’t need to know)
- Seamless queries capabilities – application layer doesn’t care from where data is coming when queries the data (it could be DB, Files, Web Service, etc.
For example, if you want to implement a Repository pattern then you need to add interface in the core components and implementation in the infrastructure. So, in case underlying data store changes in future you need to update only infrastructure layer and the core component remains intact.
Each layer contains the following:
- Domain layer
- Aggregates (for DDD)
- Value objects
- Application layer
- Application logic
- CQRS (Command and Queries)
- Exceptions handling for application logic
- Infrastructure layer
- Web Services
- Message Bus
- Presentation layer
- Authentication, Authorization
- Web API controllers
- MVC controllers
- Swagger, NSwag
The domain layer contains enterprise logic. It contains models that are persistent ignorant. You can combine domain layer and application layer if you are not using DDD or ORM (Object Relational Mapping) tools like Entity Framework, NHibernate, etc. If you are using ORM, then infrastructure needs to reference domain models so it is better to split them up in that case.
The application layer contains business logic. The enterprise logic defined in domain layer could be shared with many systems, but the business logic defined in application layer is typically used within this system.
It provides mapping from your domain model to one or more DTOs or ViewModels. Model validations also goes into this layer.
It implements interfaces defined in application layer, to provide access to external systems/data/services. It will be hooked up by IoC containers in the presentation layer.
Typically, the presentation layer has a reference of infrastructure layer to register the dependency with the IoC containers. However, there are ways to avoid it (refer Autofac: Home).
It’s the entry point for the user. Its primary job is to register all dependencies in IoC container and route the request to the application layer. Also contains authentication, authorization and controllers.
It resides with the presentation layer as it’s another entry point to the system. Because all infrastructure is abstracted by interface, mocking is very easy.
Clean Architecture Variations
- Like I said previously, if not using DDD or ORM you can combine Domain and Application layer
- You can use CQRS or normal services as an entry point for the application layer
- You can add more segments in the outer most layer (add more circles on top of outer most layer) like:
- Persistent layer
- DB Layer
- External Interfaces
- Device layer
- Plugins layer
.NET Code Template
It has the following dependencies:
- .NET Core SDK (3.1 or later)
- NodeJs (6 or later)
Run following commands to create basic .NET Clean Architecture template.
dotnet --list-sdks //Check .NET versions node -v //check nodejs version dotnet new --install Clean.Architecture.Solution.Template //Install clean architecture template mkdir clean-architecture cd clean-architecture dotnet new ca-sln //Create solution in the current directory with all template files and folder structure //You will get the following message once the creation is done //The template "Clean Architecture Solution" was created successfully. cd src/WebUI dotnet build //Build the WebUI and modules it depends on dotnet run //Run it when build is successful //If everything goes successfully, it opens up port 5001 in localhost to listen the request //Open the url http://localhost:5001 in your favorite browser to check
The folder structure this template generates looks something like the following:
The domain project contains entities, value objects, enums, events, exceptions and common modules. It contains enterprise or domain logic.
The application layer contains commands and queries using CQRS implemented for each use case. This layer implements business logic and depends on the Domain layer. It has interfaces that are implemented by outer layers.
The infrastructure layer contains classes for accessing resources like web services, files, etc. These classes are based on the interfaces defined in the application layer.
The presentation layer (WebUI) is SPA (Single Page Application) based on Angular and ASP.NET Core. It depends on the application layer and the infrastructure layer. The dependency on the infrastructure layer is only to support dependency injection.
Again, the Clean Architecture is not a new architecture by any means. It just tweaks N-tier architecture to get the following things:
- Testable – easy to test independent layers
- Keep UI separate – Devoid logic from UI so tomorrow I can switch to ReactJS or maybe VueJS
- Keep data access separate – So my application logic has no effect if I switch to different data store tomorrow (I can switch to PostgreSQL from Oracle or MS SQL Server)
- Independent of underlying frameworks or tools – like I can change validation framework
- Separate anything external to core – so that the domain layer and the application layer are kept intact (long lasting, some last 2 years and some last 10 years)
I’ve intentionally presented a simple design with 3 circles, to keep it simple and easily understood. You might need more circles for complex designs.