Clean Architecture is a software architecture approach that Robert C. Martin (Uncle Bob) described in 2012 and expanded in his 2017 book of the same name. Its goal is to keep an application's business rules independent of the database, the user interface and the frameworks in use.
The dependency rule
Clean Architecture has one rule that cannot be broken: source code dependencies point only inwards. Code in an inner circle knows nothing about anything in an outer circle. If a class, function or data structure is declared in an outer layer, an inner layer cannot refer to it.
When an inner layer has to talk to the outside world, for example to write a record to the database, the Dependency Inversion Principle comes in: the inner layer declares an interface and the outer layer implements it. At run time the flow of control goes outwards, but the source code dependency points inwards.
- Entities
- Enterprise-wide business rules
- The layer that changes least
- Use Cases
- Application-specific business rules
- Orchestrate the flow between entities
- Interface Adapters
- Controllers, presenters, gateways
- Convert data between the inner layers and external tools
- Frameworks and Drivers
- Database, web framework, user interface
- The details stay in this circle
Source: Robert C. Martin, "The Clean Architecture" (2012) and Clean Architecture (Prentice Hall, 2017). Martin stresses that the circles are schematic and need not be exactly four; what does not change is the direction of dependencies.
Advantages
- Dependencies under control: Business logic is independent of data access and external services, so a change in one layer does not leak into the others.
- Testability: Use cases are unit tested without a database or web server, with external dependencies replaced by mocks or stubs. A create-order scenario can be tested without touching the database at all.
- Resilience to change: Moving from a relational database to NoSQL or replacing a third-party service affects only the code in the outer layer; the business rules stay where they are.
- Easier maintenance: Each layer has a clear responsibility, so adding a feature or fixing a bug stays within the relevant layer. Developers who join the team find their way around the code faster.
- Reuse: Business rules that do not depend on a framework can be reused in another application or service.
Disadvantages
- Learning curve: Layers and dependency management can feel complex at first to developers who are new to this structure.
- Initial cost: Separating the layers, declaring interfaces and moving data between layers takes extra time at the start of a project. In long-lived projects this investment pays off.
- Overkill for small projects: In a small application with a few screens and simple business rules, this many layers can be heavier than needed (overengineering).
Ardalis Clean Architecture
One of the starter templates widely used in the .NET community is the Clean Architecture template that Steve Smith (ardalis) publishes on GitHub. It adapts Martin's principles to ASP.NET Core and splits the solution into Core, UseCases, Infrastructure and Web projects. Shared base classes such as the entity base, domain events and the repository interface live in a separate SharedKernel package. The template is installed with the dotnet new command and gives you a skeleton that is ready to run: the principles are the same, some of the decisions are already made.
The layer structure in our projects
In our ASP.NET Core projects we apply Clean Architecture by splitting the solution into seven projects. The names are our team's convention; the rule is still the same: dependencies flow towards Core.
Core
Entities, value objects, domain services and business rules. It does not depend on the other layers or on infrastructure packages; it is the most stable part of the project.
Application
Use cases, application services and data transfer objects (DTOs). It processes incoming requests with the business rules and reaches the database and external services not directly but through interfaces declared in the inner layer.
Infrastructure
Data access (repository implementations and migrations), external API clients and integration with third-party libraries. It implements the interfaces declared by the inner layers.
DependencyInversion
The layer where dependency injection (DI) and IoC registrations are gathered: which interface works with which class and the lifetime of each service are decided here. Replacing a data source or an external service often means changing only these registrations.
SharedKernel
Common building blocks that do not belong to the domain but are used by several layers: error types, events and helper classes. It is kept independent so that it can be reused across projects.
UI
The API endpoints or the MVC layer. It receives the request and passes it to the Application layer; it carries no business rules, so a change in the interface does not affect the business logic.
Tests
Unit and integration tests. Business rules are verified by unit tests without external dependencies; data access and service integrations by integration tests.
What this structure gives us
- Readability: Each layer has a clear responsibility, so it is predictable where things live in the code.
- Central dependency management: Adding a new service or external resource is limited to a registration in the DependencyInversion layer.
- Reuse: We reuse the common pieces in SharedKernel across projects.
- Safe change: Core and Application are tested on their own, so a change to the business rules is verified without breaking data access or the interface.
- Ready for microservices: An application with clearly drawn boundaries is the easiest one to split into its modules when that becomes necessary.


