When creating a new project, it is always a challenge to design a clean, coherent and modular architecture. There are guidelines out there to help us achieve this goal but the implementation is not always straightforward. In this blog post, I will propose an implementation of the Uncle Bob’s Clean Architecture on an ASP.Net project. The source code of this project can be found on my GitHub.
The main idea of the clean architecture is to reduce the coupling between the core business code and the external world (Web, Database, Frameworks). In order to do that, the project can be divided in 3 main modules, that will be described below:
This module contains all the core business code. It does not depend on anything else than the .NET SDK and contains sub-modules.
They are the building blocks of the domain and encapsulate the business concepts. Entities are not coupled with any ORM framework. Indeed the domain model can be quite different from the database model!
Following the Uncle Bob’s definition, they contains application specific business rules and orchestrate the flow of data to and from the entities and implement higher level business rules.
It is important to understand that the domain model should not leak outside of this module. In order to do that, use cases should not take entities are arguments for their methods, but a list of raw arguments. For instance, the use case that submit a new expense looks like the following:
1 2 3 4
It is responsible to build an
Expense object and apply the relevant business logic.
In the domain module, repositories are only interfaces that are used by the use cases to access data without any knowledge of the concrete implementations: data can be retrieved from databases, files or external Web APIs, it should not affect the core business logic at all. To reinforce this, repositories take entities as arguments and return entities as well. It is the responsibility of the concrete implementation to handle conversion if needed.
The data module contains the database and ORM configuration and the repositories implementations. Typically, we find the EntityFramework configuration in this module as well as the annotated classes that will be mapped with database entries. Repositories implementation are responsible for converting “database objects” in domain objects:
1 2 3 4 5 6
This prevents the ORM framework to leak into the domain.
The web module contains all the controllers. It is responsible for handling HTTP requests, converting JSON or XML payloads to objects and invoking use cases:
1 2 3 4 5 6
It should not contain any business logic whatsoever. As a consequence, controllers are very lightweight and easy to test.
In order to achieve low coupling between the modules, interfaces are injected into the constructor of the different classes:
1 2 3 4 5 6 7 8 9 10
The plumbing is handled by the
Startup.cs class where all the implementations of the interfaces are declared:
1 2 3 4 5 6 7 8
In this architecture, the emphasis is put on the domain. Every other modules should adapt to it but it does not depend on anything. This results in lightweight classes that are very easy to understand and test in isolation.
Moreover, the low coupling makes it very easy to change the infrastructure. Indeed, the domain module would not change a bit if we decided to have a CLI instead of a Web App or if the data should be retrieved from an external Web API instead of a PostgreSQL database.