The Layers of Clean Architecture
Entities, use cases, interface adapters, frameworks & drivers - the concentric circles, and how a use-case interactor orchestrates a single application operation.
On this page
Robert Martin's Clean Architecture draws the same Dependency Rule as four concentric layers, and it adds one concept hexagonal leaves implicit: the use case (or interactor) as an explicit object representing a single application operation. Where hexagonal emphasizes the boundary (ports and adapters), Clean emphasizes what lives in each ring - and especially that thin layer of application-specific logic between the domain and the edge.
The four rings
From the center out:
- Entities (innermost) - enterprise-wide business objects and rules. Your DDD entities, value objects, and aggregates live here. They'd be true even if this particular app didn't exist. Depend on nothing.
- Use Cases - application-specific business rules. Each use case orchestrates entities to accomplish one operation ("transfer money," "post transaction"). Depend only on entities.
- Interface Adapters - translators: controllers, presenters, repository implementations that convert between the use-case world and the outside. Depend inward on use cases.
- Frameworks & Drivers (outermost) - Spring, JPA, the database, the web server. Glue and configuration. The volatile edge.
The rule is unchanged: nothing in an inner ring knows about an outer ring. An entity never imports a use case; a use case never imports a controller or Spring.
The use-case interactor
The concept worth internalizing is the use case as an object. Instead of business logic smeared across fat controllers and services, each application operation is one focused class with one method:
// The use case: one operation, orchestrating entities via ports
class TransferMoneyUseCase implements TransferMoney { // driving port (inbound)
private final AccountRepository accounts; // driven port (outbound)
private final TransactionPublisher events;
public TransferResult transfer(AccountId fromId, AccountId toId, Money amount) {
Account from = accounts.findById(fromId).orElseThrow();
Account to = accounts.findById(toId).orElseThrow();
from.withdraw(amount); // entities enforce their invariants
to.deposit(amount);
accounts.save(from); accounts.save(to);
events.publish(new MoneyTransferred(fromId, toId, amount));
return TransferResult.success();
}
}Notice what it is and isn't. It is the application's business rules: the sequence of steps to transfer
money, coordinating entities and ports. It isn't HTTP (no @RequestMapping, no JSON), isn't SQL (it uses
the AccountRepository port), and isn't presentation. Reading it tells you exactly what the operation does,
free of technical noise. This is where hexagonal's "driving port" gets a body.
Entities vs. use cases
A subtle but important split: entity logic is rules that are true regardless of the application ("an account
can't go negative"); use-case logic is application-specific orchestration ("to transfer, load both accounts,
move the money, publish an event"). The invariant lives on the Account entity; the workflow lives in the
TransferMoneyUseCase. Keeping them separate means the entity is reusable across use cases, and the use case
reads as a clear script.
Crossing boundaries: talk in simple structures
When data crosses a boundary - a controller handing input to a use case, a use case returning a result - pass simple, boundary-appropriate structures (a request/response DTO or a plain record), never an outer-layer object inward. The controller maps HTTP JSON to a use-case request object; the use case returns a plain result the presenter formats. This keeps the dependency pointing inward and stops framework types from leaking into the core. It's the same 'map at the edge' idea the next lesson develops.
Entities are the recipes - true whether this restaurant exists or not (a béarnaise is a béarnaise anywhere). Use cases are the head chef's process for one order: 'fire the steak, plate with béarnaise, garnish, send' - application-specific orchestration of recipes, but still pure kitchen craft, ignorant of how the order arrived. Interface adapters are the waiters translating between the dining room and the kitchen (taking your spoken order, writing a ticket the chef reads; carrying plates out). Frameworks & drivers are the building, the POS system, the gas lines - swappable infrastructure. The chef's process never depends on which POS the waiters use, and the recipe never depends on the chef - dependencies point inward.
For a 'close a monthly billing period' feature, four pieces of logic exist: (a) the rule that a period's total
must equal the sum of its invoices; (b) the orchestration: load the period, verify all invoices are finalized,
mark it closed, emit a PeriodClosed event; (c) parsing the HTTP POST /periods/{id}/close request; (d) the SQL to
update the period row. Assign each to a Clean Architecture ring and note which must not depend on which.
What is a 'use case' (interactor) in Clean Architecture?
Key takeaways
- Clean Architecture draws the Dependency Rule as four rings: Entities, Use Cases, Interface Adapters, Frameworks & Drivers - inner never knows outer.
- Entities hold enterprise-wide rules (true without this app); use cases hold application-specific orchestration of those entities.
- A use case (interactor) is one operation as an object - it coordinates entities via ports and contains no HTTP, SQL, or framework code.
- Data crossing a boundary should be simple structures (request/response DTOs), never outer-layer objects passed inward.
- It's the same idea as hexagonal - Clean emphasizes what lives in each ring and the explicit use-case object; hexagonal emphasizes ports and adapters.