Clean Architecture is a domain-centric way to organize a system into four layers: Domain, Application, Infrastructure, and Presentation. All dependencies point inwards, so the core domain stays independent of the UI, databases, and external services. It pays off when the business logic is complex, and it can be over-engineering on a small project.
Software Design Simplified .NET Edition is a short and practical book for junior and mid-level engineers. You will learn encapsulation, cohesion, polymorphism, and more in one afternoon. Check it out here.
IcePanel realigns your engineering & product teammates on the technical decisions made across the business. Explain your complex software systems with an interactive map that teammates can drill down to the level of detail they need.
I've been using Clean Architecture for 6+ years on large-scale applications serving thousands of customers and millions of requests. Today I want to talk about why it's a great approach for structuring your applications.
I'm aware that Clean Architecture isn't a silver bullet, so I will discuss what types of systems can benefit from this architecture.
Clean architecture isn't revolutionary.
But it's prescriptive about how you should structure the code.
It's an evolution of layered architecture, focusing on the core domain and the direction of dependencies. All dependencies should point inwards, applying dependency inversion.
Here are some of the promises of Clean Architecture:
- Maintainability
- Testability
- Loose coupling
- Separation of concerns
It's independent of UI, databases, or external services - but you also need to be pragmatic (more on this later).
Let's dive in!
What Is Clean Architecture?
Clean Architecture was created by Robert C. Martin, aka Uncle Bob.
Clean Architecture is an approach to organizing a software system to separate the concerns of the various components. Making the system easier to understand and maintain.
You can think about Clean Architecture as a domain-centric approach to organizing dependencies.
There are similar architectures that follow the same domain-centric idea. You may know them as the Hexagonal and Onion architectures. They are more or less interchangeable with each other. And they all place the core domain at the center of the architecture.
This is my high-level interpretation of Clean Architecture:
There are four layers inside:
- Domain
- Application
- Infrastructure
- Presentation
Let's see what should live inside each layer.
Clean Architecture Layers
Here's a breakdown of what should live inside each layer of the Clean Architecture.
Domain
- Contains the core business rules & logic
- It should be independent of other layers in the system
- Should be persistence ignorant - the persistence mechanism shouldn't influence your domain model
- Examples: Entities, Value Objects, Domain services, Domain events, Enums, Repository interfaces
Application
- Contains the application use cases
- Contains application-specific business rules
- Orchestrates the domain entities to perform business operations
- It should be independent of external concerns (but it doesn't have to be)
- Examples: Application services, Commands, Queries, External service interfaces, Exceptions
Infrastructure
- Contains anything related to external concerns
- Implements interfaces defined in the layers below
- Examples: PostgreSQL, Keycloak, AWS S3, RabbitMQ, Kafka, SendGrid
Presentation
- Represents the entry point to the system
- Accepts data from the outside and passes it to the use cases
- Acts as the composition root for dependency injection
- Examples: ASP.NET Core, gRPC
Here's an example of how to structure the Clean Architecture on a solution level. You can also group related components together by feature. It leads to better cohesion and is an excellent option if your project is more complex.
I also made a few videos covering the Clean Architecture project setup:
- Clean Architecture walkthrough (100k+ views)
- Clean Architecture project setup from scratch (40k+ views)
Where Should You Use Clean Architecture?
Clean Architecture is very versatile and applies to various domains and systems.
But, you should play to its strengths and use it only when there's a tangible benefit.
I use the Clean Architecture when I want to:
- Apply Domain-Driven Design
- Solve complex business logic
- Build highly testable projects
- Enforce design policies via the architecture
If the above is true for your project, then Clean Architecture is an excellent option.
You should also consider the benefits of Clean Architecture.
The Case For Being Pragmatic
I try to be pragmatic when using Clean Architecture.
Applying what I like and having the freedom of "breaking" Clean Architecture if it will simplify things.
Can this be called Clean Architecture, then? No, not in the purest sense.
But, I still get most of the benefits of Clean Architecture.
Here's an example, when I'm applying CQRS in Clean Architecture to implement the use cases.
On the command (write) side, it's valuable to be independent of external concerns, so I will use repositories behind an interface. I can control the repository contract, and unit testing is straightforward.
But, on the query (read) side, I want to return the response as fast as possible. Creating an abstraction only adds indirection and reduces performance.
A better approach is to use the EF or Dapper in the handler and query the database. It's simple, fast, and you can use all the features offered by the ORM. You don't need a lot of complexity or abstractions on the query side.
I should call this approach Pragmatic Clean Architecture.
Closing Thoughts
Clean Architecture gives you a standard for organizing your solution.
You don't have to reinvent the wheel every time at the start of the project.
But, the layered structure and architectural constraints can increase the complexity of smaller projects.
So make sure your project is complex enough to apply Clean Architecture.
Another caveat of Clean Architecture is the danger of over-engineering.
Don't follow the principles religiously without considering the specific project requirements.
The overhead of maintaining so many layers and abstractions may not be justified.
Be pragmatic and try to make the best decision possible.
Sometimes that means straying from the paradigm.
Hope this was helpful.
I'll see you next week!
Frequently Asked Questions
What is Clean Architecture?
Clean Architecture, created by Robert C. Martin, is a domain-centric way of organizing a software system to separate concerns. It evolves layered architecture by placing the core domain at the center and pointing all dependencies inwards through dependency inversion. It promises maintainability, testability, loose coupling, and separation of concerns.
What are the four layers of Clean Architecture?
Domain holds the core business rules and stays persistence ignorant. Application contains the use cases and orchestrates domain entities. Infrastructure implements external concerns like databases, message brokers, and email services. Presentation is the entry point, such as an ASP.NET Core API, and acts as the composition root.
What is the difference between Clean Architecture, Onion Architecture, and Hexagonal Architecture?
They follow the same domain-centric idea and are more or less interchangeable. All of them place the core domain at the center of the architecture and direct dependencies inwards. The differences are mostly in naming and presentation rather than substance.
When should you use Clean Architecture?
Use it when there is a tangible benefit: applying Domain-Driven Design, solving complex business logic, building highly testable projects, or enforcing design policies through the architecture. For smaller projects, the layered structure and architectural constraints can add complexity that is not justified.
Is Clean Architecture over-engineering?
It can be. The danger is following the principles religiously without considering project requirements, since maintaining many layers and abstractions has real overhead. Be pragmatic: breaking the rules where it simplifies things still keeps most of the benefits.
Do you need repository abstractions on the query side of CQRS?
Usually not. On the command side, repositories behind an interface keep you independent of external concerns and easy to unit test. On the query side, an abstraction only adds indirection and reduces performance, so querying the database directly with EF or Dapper in the handler is simpler and faster.



