During their software development career, many developers face a situation where the project starts at a rapid pace but then the growth slows down or completely stuck due to the project architecture chosen at the start. Adding new features becomes hard and time-consuming and timely delivery of those features to market also becomes slow. A new feature or bug fix produces more new bugs and the backlog grows faster than the team can handle. It also becomes very difficult to get rid of outdated libraries, databases, or frameworks without significant development effort. To solve all these problems, many architectures have been developed over the years, and in this tutorial, we will learn one of the most popular architectures called clean architecture.
Table of Contents
Why do we need to architect?
The goal of software architecture is to minimize the human resources required to build and maintain the required system.Robert C. Martin, Clean Architecture
Software architecture is the foundation of a software system. Like any other engineering field, if the foundation is not solid, you can’t guarantee the quality of what is built on top of it. When this foundation is built, an architect needs to take several important decisions for software quality, maintenance, and successful delivery in the future. The greater the size and complexity of a software system, the more you will need a well-thought-out architecture to succeed. A good software architecture provides several benefits such as:
- Testable: A good software architecture enables fast and reliable tests that are easy to write, execute and maintain.
- Maintainable: A good software architecture makes it easier to maintain existing software as the structure of the code is visible and well-known.
- Changeable: A good software architecture allows us to accommodate changes and upgrades with minimum effort and impact.
- Easy to Develop: A good software architecture not only reduces the complexity of the system but also reduces code duplicity.
- Easy to Deploy: A good software architecture encourages loosely coupled services and components which makes deployment easy.
What is Clean Architecture?
Clean Architecture is introduced by Robert C. Martin (also known as Uncle Bob) in 2012 and has gained popularity in recent years. It is derived from many architectural guidelines like Hexagonal Architecture, Onion Architecture, etc. and it emphasizes the separation of concerns and maintainability of code. In this architecture, the business logic is kept separate from the infrastructure and presentation layers, which allows developers to build scalable, testable, and maintainable software.
The Domain and Application layers are the center stage of the Clean Architecture and are often known as the Core of the System. The Domain layer contains enterprise logic and types and the Application layer contains business logic and types. The difference is that enterprise logic could be shared across many systems, whereas business logic will typically only be used within a system. The Infrastructure layer contains data access or other infrastructure concerns and dependencies flow inwards. The Core should not be dependent on the Infrastructure layer rather Infrastructure layer depends on the Core. This functionality is achieved by defining abstractions, or interfaces within Core, which are then implemented by types defined in the Infrastructure layer.
Key Principles of Clean Architecture
The clean architecture combines many software design principles and practices in a single architecture. Some of the key principles of clean architecture are as follows:
Separation of Concerns
Clean architecture organized the code into layers in such a way that each layer is responsible for a specific part of the application. All layers of the system are independent or decoupled, allowing us to introduce a change and test business logic or user interfaces without impacting other layers or areas of the application.
Dependency Inversion Principle (DIP)
In clean architecture, the high-level modules are defined close to the domain or business logic, and low-level modules are defined close to the input and output of the program. The dependency inversion principle states that high-level modules should not depend on low-level modules. The dependencies should be inverted towards the inner layers. In other words, the abstractions should not depend upon details, the details should depend upon abstractions.
Single Responsibility Principle (SRP)
The single responsibility principle states that every module, class, or function in the code should have only one responsibility and only one reason to change. It makes your software easier to implement and prevents unexpected side effects of future changes.
The code should be open for extension but closed for modification. This means that if your business requirements change, your existing code should not be changed (closed for modifications). Instead, you should add a new code by extending the old code (open for extension). This principle makes your code more stable because you can add new features without affecting the existing code.
Layers of Clean Architecture
The clean architecture separates the concerns into distinct layers which makes code easier to test, maintain, and modify. Each layer can be developed and tested independently, without affecting the other layers.
This layer is the backbone of the clean architecture and it represents the core, use-case-independent business logic of the system/domain. This layer is highly abstracted and stable and it should not have any dependency on any external library or frameworks. It includes interfaces, entities, and value objects. All other projects should depend on the Domain layer.
This layer contains
- Domain Entities
- Value Object
- Domain Events
This layer implements the business logic and the use cases of the application. It is dependent on the domain layer but has no dependency on the infrastructure and presentation layers. Mostly it defines interfaces that are implemented by the outer layers of the clean architecture.
This layer contains
- Business Services
- Commands and Queries
- Application Exceptions
- Request and Response Models
- Domain Entities to DTOs mappers
This layer contains the implementation of the interfaces defined in the Application layer. It can also contain abstractions and integrations to third-party libraries and services. The majority of your application’s external dependencies are included in the infrastructure layer. It is also very common practice to create multiple infrastructure projects in this layer especially if you are working on a very large project.
This layer contains
- Authentication and Identity Services
- File/Object Storage
- Message Queue Storage
- Third-Party Services
- Email and Notification Services
- Logging Services
- Payment Services
- Social Logins
This layer handles database and caching-related operations. This layer implements the interfaces and repositories defined in the Application layer using specific frameworks or libraries e.g. Entity Framework, Dapper, etc. This layer also contains dependencies related to specific databases e.g. SQL Server, Oracle, etc.
This layer contains
- Data Context
- Data Migrations
- Data Seeding
- In Memory Caching
- Distributed Caching e.g. Redis, Memcached, etc.
This layer is normally responsible for presenting some Graphical User Interface (GUI) interface or exposing some public Web APIs with whom users or other client applications interact. This layer depends on both the Application and Infrastructure layers.
This layer contains
- Web Pages
- Web Components
- Web APIs
- View Models,
- Style Sheets
- Java Script Files
If you want to learn by doing then read my post Building ASP.NET Core Apps with Clean Architecture in which I will explain each of the above layer in detail.
Benefits of using Clean Architecture
Clean architecture gives us these benefits:
Independent of Frameworks
Clean architecture does not depend on the existence of libraries or frameworks. Frameworks are used as tools and implemented inside individual modules in isolation. This means if you change your mind, you can easily swap one framework with another, and the rest of the app should work just fine without any change.
Independent of Database
In clean architecture, the business rules are not bound to any database logic. The database is treated just like any other data provider and our application has real use cases rather than being a CRUD system.
Independent of User Interface
In clean architecture, the application core has no dependency on the front-end UI. You can easily replace your front end from a website to a Mobile, Windows, or Console application. You can also change the front-end application framework from ASP.NET MVC to Angular, Vue or Angular, etc. without affecting business rules or the rest of the application.
In clean architecture, your application core does not depend on the outer layer which means it can be easily and quickly tested in isolation without worrying about the implementation details. The business rules can be tested without the UI, Database, Web Server, or any other external element.
Challenges of using Clean Architecture
Implementing and maintaining a clean architecture can be challenging in some situations. Following are some of the common challenges that you may face:
There is a lot of upfront design work involved to implement clean architecture with a clear separation of concerns between layers. This makes the architecture more complex and it may take some time to get it right. There is also a risk of over-engineering when implementing clean architecture so it is important to keep simplicity and abstraction in balance to avoid unnecessary complexity.
Developers and architects who are used to working with other architectures sometimes find it difficult to understand and implement clean architecture without proper education or training. They don’t split the responsibilities between layers properly which makes architecture difficult to understand especially for new or junior developers.
Lack of Tools
There are not many specialized tools available that can help or automate the process of implementing clean architecture. Some development tools and frameworks don’t work well with clean architecture. For example, some ORMs tightly couple the domain model with the data access layer and make it difficult to separate them cleanly.
Clean Architecture Sample Projects
Following are some example projects and open-source templates that use the Clean Architecture.
- Clean Architecture Solution Template for .NET 7
- Clean Architecture with ASP.NET Core
- BlazorHero – Clean Architecture Template for Blazor WebAssembly
- Clean Architecture in Node.js with TypeScript
- Clean Architecture in Angular
- Clean Architecture in Android with Kotlin
- Clean Architecture in React with Redux
Clean Architecture can help developers to build scalable, maintainable, and testable applications by separating concerns into distinct layers. If you are building a new application, then consider using Clean Architecture if it suits your needs. Similarly, if you have a big and complex application with a lot of business logic then using clean architecture can give you a lot of benefits. I hope you’ve enjoyed this tutorial on Clean Architecture. If you have any comments or suggestions, please leave your comments below. Don’t forget to share this tutorial with your friends or community.