Clean Architecture: Complete Definition and Guide
Définition
Clean Architecture is a set of software design principles proposed by Robert C. Martin (Uncle Bob) that organizes code in concentric layers. Business logic sits at the center, independent of any framework, database, or user interface.What is Clean Architecture?
Clean Architecture is a software architecture style proposed by Robert C. Martin (Uncle Bob) in 2012. It follows in the tradition of Alistair Cockburn's hexagonal architecture (ports and adapters) and Jeffrey Palermo's onion architecture. Its fundamental principle is separation of concerns through concentric layers, with the dependency rule: outer layers depend on inner layers, never the reverse.
At the center sits pure business logic, with no dependencies on implementation details (web framework, database, external API). This independence ensures that the application's core remains stable and testable even when peripheral technologies change.
Why Clean Architecture Matters
Clean Architecture addresses the most common problems in growing software projects: spaghetti code, circular dependencies, inability to test, and increasing cost of change.
- Framework independence: the framework (Django, FastAPI) becomes a replaceable implementation detail. Business logic doesn't depend on Django; Django depends on business logic.
- Testability: business logic can be tested without a database, web server, or user interface. Tests are fast and reliable.
- Database independence: switching from PostgreSQL to MongoDB or an external API doesn't impact business logic.
- Maintainability: code is organized by business intent, not by technical layer. Each change is localized and predictable.
- Flexibility: adding a new interface (mobile API, CLI, webhook) doesn't require modifying existing business logic.
- Longevity: business code survives framework and technology changes.
How It Works
Clean Architecture is organized in four concentric layers. The innermost layer, Entities, represents fundamental business objects and universal business rules that exist independently of any application. For example, the rule "a quote expires after 30 days" is an entity rule.
The Use Cases layer contains application-specific logic. Each use case orchestrates entities to perform a business action: "Create a quote," "Accept an order," "Calculate invoice amount." Use cases define the ports (interfaces) they need (repository for data access, gateway for external services).
The Interface Adapters layer contains translators between the application's internal format and the format expected by external systems. Controllers (Django views), presenters (serializers), and repositories (data access implementations) reside in this layer.
The outermost layer, Frameworks & Drivers, contains technical details: Django itself, PostgreSQL, Redis, external APIs. These elements are interchangeable without impacting inner layers.
The fundamental rule is the dependency rule: dependencies always point inward. A use case doesn't know about Django; it knows a "Repository" interface that Django implements.
Concrete Example
Kern-IT applies Clean Architecture principles in an invoicing platform project. The entities layer defines business concepts: Invoice, LineItem, Customer, with their validation rules (total amount is the sum of lines, VAT is calculated based on customer's country).
The use cases layer contains cases like CreateInvoice, SendInvoice, MarkAsPaid. The CreateInvoice use case receives input data, validates business rules, creates the Invoice entity via the repository, and notifies via the notification gateway. It doesn't know whether the repository uses PostgreSQL or a remote API.
The adapters layer implements DjangoInvoiceRepository that persists invoices in PostgreSQL via Django's ORM, StripePaymentGateway for payments, and Django REST Framework views as controllers. If tomorrow the company switches from Stripe to Mollie, only the gateway changes, not the business logic.
Implementation
- Identify business entities: define fundamental domain objects and their intrinsic validation rules.
- Define use cases: list the application's business actions, each with its inputs, outputs, and rules.
- Create interfaces (ports): define abstract contracts that use cases need (repositories, gateways, notifiers).
- Implement adapters: create concrete implementations of interfaces using Django, PostgreSQL, external APIs.
- Wire with dependency injection: use a dependency injection container or simple factory to connect implementations to interfaces.
- Test each layer: pure unit tests for entities and use cases (no framework), integration tests for adapters.
Associated Technologies and Tools
- Django: framework that lends itself well to Clean Architecture by separating models (adapters) from business logic (use cases/services).
- Python dataclasses / Pydantic: for defining business entities as simple objects without framework dependencies.
- pytest: Python testing framework that facilitates fast unit tests of use cases.
- dependency-injector: Python library for dependency injection.
- ABC (Abstract Base Classes): standard Python module for defining interfaces/ports.
- mypy: static type checking to ensure interface compliance.
Conclusion
Clean Architecture is an investment in the longevity and quality of your code. It's not suited to every project: an MVP or small internal tool doesn't justify this structural complexity. But for critical business applications that must evolve over years, Clean Architecture ensures code remains understandable, testable, and adaptable. The key is understanding the underlying principles (dependency inversion, separation of concerns) and applying them to the appropriate degree for your context.
Don't apply Clean Architecture dogmatically. For a Django project, a good compromise is creating a service layer (services.py) that contains business logic, separated from views and models. That's 80% of Clean Architecture's benefits with 20% of the complexity.