Mastering Clean Architecture: Principles and Practical Insights
In this issue, we will discuss Clean Architecture. What is it, when should you use it, and when should you not? We will find out why vertical slice architecture is necessary, and we will also see some essential learning resources (more than 40) on clean architecture and domain-driven design.
So, let’s dive in.
Postman Postbot (Sponsored)
Postbot is an AI-powered assistant that will supercharge your API development. Powered by Postman, it speeds up your most common API development with natural-language input, conversational interactions, and contextual suggestions. Get started using instant documentation, easier scripting, and instant support features to speed up your API development and collaborations.
The Clean Architecture philosophy emphasizes the importance of separating concerns in software design and creating modular, testable, and maintainable code. It was developed by a software engineer and consultant, Robert C. Martin, and introduced in 2012. in this blog post.
At its core, Clean Architecture promotes the idea that software systems should be designed to be understood and maintained by developers over the long term. To achieve this goal, Clean Architecture proposes a layered architecture with clear boundaries between different system components to achieve independence of frameworks, UI, databases, and delivery mechanisms and the possibility to test in isolation. Clean Architecture borrows ideas from Hexagonal Architecture, also known as Ports and Adapters, which emphasizes separating business logic from external dependencies. This architectural pattern facilitates easier testing and promotes flexibility by decoupling the core application from external frameworks and libraries.
The Clean Architecture philosophy defines a set of layers, starting with the most general and abstract layers and moving toward the most concrete and specific layers. These layers include:
- Entities. The system’s fundamental objects represent the core business logic and data. They encapsulate the most general and high-level rules.
- Use cases involve high-level interactions between the system and its users or other external systems containing application-specific business rules. This layer is not expected to affect the entities or external systems.
- Interfaces. The mechanisms by which the system communicates with external systems or users. Here, we can have an MVC architecture of a GUI.
- Controllers. The components manage the data flow between the other system layers.
- Presenters. The components are responsible for presenting data to users or other systems.
- Infrastructure. The components are responsible for interacting with external systems or services, such as databases or APIs.
An application structure with Clean Architecture could look like this:
Compared to Onion Architecture, Clean Architecture offers a more precise separation of concerns and a better comprehension of boundaries. They support similar ideals but with various levels and are close relatives.
When should you use Clean Architecture?
We should use Clean Architecture when:
- Building complex or long-lived applications where maintainability, testability, and scalability are crucial.
- It’s suitable for projects where the domain model is central to the application’s functionality and needs to be well-defined and encapsulated.
- Clean Architecture benefits teams that prioritize clean code practices, separation of concerns, and a domain-driven design approach.
When should we not use Clean Architecture?
- Clean Architecture might introduce unnecessary complexity and overhead for small or simple projects with straightforward requirements. It introduces increased complexity, with more layers and abstractions, which can make the codebase more complex.
- Projects with tight deadlines or limited resources may not benefit from the upfront investment required to implement Clean Architecture.
- If the project’s requirements are likely to change frequently or the domain model is not well-understood, a more flexible and agile approach might be more appropriate.
- Teams unfamiliar with the architecture: If the development team is not familiar with the principles of Clean Architecture, the learning curve could lead to implementation mistakes and slow development.
Clean architecture makes it crystal clear why each layer is necessary and what their roles are, which is why it is often referred to as Screaming architecture (along with Onion and Hexagonal architectures).
The term “Screaming Architecture” suggests that the architecture should “scream out” its intent and purpose, making it evident to developers, stakeholders, and anyone involved in the project. Here we want to keep domain related code separate from technical details.
For example, if you’re building an e-commerce platform, someone looking at your codebase should immediately see elements like “ShoppingCart,” “ProductCatalog,” and “CustomerProfile” instead of just “Controllers,” “Services,” and “DTOs.”
This is achieved by following principles such as:
- Domain-Centric Design: The architecture should reflect the domain and business logic of the system, making it easy to understand how the software solves real-world problems.
- Clear Separation of Concerns: Different parts of the system should have clear and distinct responsibilities with well-defined boundaries. This separation makes it easier to reason about and maintain the codebase.
- Dependency Inversion: Dependencies should be abstracted from the core business logic, allowing flexibility and easier testing. This principle promotes loose coupling and high cohesion within the system.
- Use of Patterns and Practices: Applying design patterns and best practices helps to standardize the architecture and make it more predictable and understandable.
How to scale your system with Vertical Slice Architecture
When using standard architectural patterns, such as the Layered or Clean architecture approach, where we have N-layers, such architecture doesn’t scale well when the application grows. We usually violate the Single Responsibility Principle (SRP), as one functionality is split across many layers. We need to know about all dependencies between layers to implement a feature or fix a bug.
With Vertical Slice Architecture, we build our architecture over different requests, grouping all concerns from the front end to the back end. Here, we couple vertically along the slice, minimizing coupling between slices and maximizing coupling in a slice (things that change together belong together).
This means we don’t need many abstractions, such as repositories, services, etc. With this approach, every slice decides how to approach a request. In addition, this approach enables us to test our application better, as the boundaries of the test become a lot cleaner. This allows us to write integration tests with little mocking that are unrelated to the feature we are testing.
The code structure of such architecture would look like this:
How to Learn Clean Architecture and Domain-Driven Design?
While conceptually straightforward, applying clean architecture effectively can have a learning curve. It requires an understanding of dependency injection and good software design practices. If you want to learn more about it, with real-life examples, here are some learning resources:
📚 Books
- “Clean Architecture: A Craftsman’s Guide to Software Structure and Design,” Robert C. Martin https://amzn.to/40XN8yt
- “Get Your Hands Dirty on Clean Architecture,” Tom Hombergs https://amzn.to/3SZrJ65