Clean Architecture
itSoftware engineering
Clean Architecture
Clean Architecture keeps business rules separate from delivery and infrastructure details. Your application still uses a web framework, database, message broker, or command line. Those tools sit outside the policy that defines what the application does.
The central idea is a direction rule. Source code dependencies point toward business policy. Inner code does not name outer tools. Outer code translates between a tool's format and the application's own models.
This structure gives you options. You can test business behavior without starting a web server or connecting to a production database. You can replace a user interface or persistence mechanism without rewriting the core rules. The architecture does not remove change. It limits how far a change can spread.
Why boundaries matter
Applications often begin with a framework-generated structure. Controllers receive requests. Data-access objects store records. Business decisions then slip into both places because they are convenient.
That arrangement works until one decision must serve another interface or use another storage mechanism. The same rule gets copied, or the new interface must call through the old delivery path. Tests also inherit setup that has nothing to do with the rule under test.
Clean Architecture draws explicit boundaries around policy. A boundary controls which concepts cross and which direction the code knows about. It separates reasons to change:
- Business rules change when the organization changes its policy.
- Application rules change when a use case changes.
- Interface adapters change when an external format changes.
- Frameworks and drivers change when a technical product changes.
The boundary does not require a separate service, process, or deployment. It can exist between functions, packages, modules, or components in one application.
The four common areas
The familiar circle diagram shows four areas. Treat them as responsibilities, not mandatory folder names.
Entities
Entities hold the most general business rules and business data. In one application, an entity may be a domain object or a group of functions and data structures. It should not change because a page layout, database library, or transport protocol changes.
Use cases
Use cases implement application-specific business rules. A use case coordinates entities to achieve one user or system goal. Examples include placing an order, approving an invoice, or closing an account.
A use case receives input through an application-owned boundary. It returns output through another application-owned boundary. It does not accept an HTTP request object or return a database row.
Interface adapters
Interface adapters convert data between external formats and application formats. Controllers translate incoming requests into use-case input. Presenters translate use-case output into a form that a view can render. Gateways implement application-owned interfaces for databases and external services.
Frameworks and drivers
The outer area contains concrete tools and delivery mechanisms. A web framework, database driver, user interface, scheduled job, and message consumer belong here. They connect the application to its environment.
The Dependency Rule
The Dependency Rule says source code dependencies point inward. Code in an inner area must not know names declared in an outer area. Inner policy therefore cannot import a controller, persistence record, or framework type.
Control flow can still travel outward. A use case may need to store data or send output. Dependency inversion handles this crossing:
- The inner area declares the interface it needs.
- An outer adapter implements that interface.
- Composition code connects the implementation at runtime.
The interface belongs near the policy that uses it. This placement keeps the source dependency pointing inward even when runtime control reaches an outer tool.
Data also crosses a boundary. Use small, purpose-specific data structures. Do not let a framework model become the application's business model by convenience.
A request from outside to inside
Consider a request to place an order.
- A controller validates transport-level details and creates use-case input.
- The place-order use case applies application rules.
- Entities enforce general order rules.
- The use case calls an order repository interface that it owns.
- A database adapter implements that interface and maps application data to storage data.
- A presenter converts the result into a delivery-friendly view model.
The database call happens during the use case, but the use-case source does not depend on the database adapter. Runtime order and source dependency direction are different ideas.
Where it fits
Clean Architecture belongs to a family of inside-outside designs. Ports and Adapters gives the application ports for purposeful conversations and adapters for specific technologies. Onion Architecture uses similar dependency direction. Clean Architecture combines these ideas into a layered model centered on policy.
The approach is useful when business behavior must outlive a particular interface or technical product. It also helps when you need fast policy tests, several delivery channels, or controlled replacement of infrastructure.
You do not need four projects for every application. A small program may need only a clear module boundary and a few interfaces. More layers add translation code and navigation cost. Add a boundary when it protects a meaningful policy from a likely source of change.
What Clean Architecture does not promise
Clean Architecture does not choose your domain model, deployment topology, or team structure. It does not require microservices. Logical layers can run in one process and deploy as one unit.
It also does not make infrastructure unimportant. Databases and web frameworks still shape performance, reliability, and operations. The point is to prevent their APIs from owning business policy.
Poorly chosen boundaries can produce empty pass-through layers. Excessive mapping can hide simple work behind ceremony. Start from use cases and change pressures. Judge a boundary by the independence it creates, not by the number of folders it adds.
A practical adoption path
Begin with one important use case. Write its input, output, and business decisions in application terms. Move direct framework and database calls behind interfaces owned by that use case or its domain.
Add tests against the inner boundary. These tests should run without a user interface, network listener, or production database. Then build thin adapters that translate between external formats and the boundary.
Review source dependencies. An inner import from an outer package exposes a broken boundary. Fix the ownership or move the translation outward.
Finally, organize the top level around business capabilities where that improves discovery. A codebase should reveal what the application does before it reveals which tools it uses.
