Clean Architecture

The four rings of Clean Architecture, the Dependency Rule, how a call crosses a boundary when control flows outward, and when the rings are too much.

Overview

Clean Architecture is the name Robert Martin (Uncle Bob) gave to a family of designs that keep business rules in the middle of a system and push the web framework, the database and the user interface to the edge. He set it out in a blog post in August 2012 and later in a book, Clean Architecture: A Craftsman's Guide to Software Structure and Design (Pearson, 2017).

The post is a synthesis, not a new invention. It lists Hexagonal Architecture (Alistair Cockburn), Onion Architecture (Jeffrey Palermo), his own Screaming Architecture, DCI and BCE, and says they share one goal: a system that is independent of frameworks, of the UI, of the database and of outside agencies, and that can be tested without any of them. Clean Architecture adds two things on top of that shared goal: it names rings inside the core, and it states one rule for source code that holds across every ring.

Interviewers use it to check three things: can you say the Dependency Rule in one sentence, can you show how a use case talks to a presenter or a database without depending on it, and do you know when the rings cost more than they save.

The four rings

From the inside out, the post names four rings. Martin says four is a sketch, not a law; a system may need more, as long as every dependency still points inward. The inner rings are the most abstract and change least often.

  • Entities — Rules that would hold even without this application — The business itself changes — Course.enroll() refuses a full course
  • Use cases — Rules specific to this application: one class per thing a user can do — The application's behavior changes — EnrollStudent loads a course, enrolls, saves
  • Interface adapters — Conversion between the use cases' data and the outside world's formats — A screen, an API shape or a schema changes — Controller, presenter, SQL gateway
  • Frameworks and drivers — The tools themselves, plus thin glue code — You swap or upgrade a tool — Flask, PostgreSQL, a message broker client

The split between the two inner rings is what Clean adds to Hexagonal. An entity holds a rule the business would follow on paper; a use case holds the steps this one application takes to apply it.

The Dependency Rule

Source code dependencies point only inward. Code in an inner ring never imports, names or calls a class, function or data format declared in an outer ring. The rule is about import lines and type names, not about which way data moves at run time.

That raises the real question: a use case must hand its result to a presenter, and the presenter is outside. Martin's answer is the Dependency Inversion Principle. The use case ring declares an interface (an output port), the presenter implements it, and the use case calls the interface. Control flows outward; the source dependency still points inward.

What crosses a boundary

Data that crosses a ring boundary is a plain data structure shaped for the inner ring: a request model going in, a response model coming out. Martin lists simple structs, data transfer objects (DTOs), function arguments or a dictionary. Two things must not cross: entities going outward, and framework objects such as an ORM row or an HTTP request going inward. Either one makes an inner ring depend on an outer ring's format.

  • Controller to use case — EnrollRequest(student_id, course_id) — The framework's request object
  • Use case to presenter — EnrollResponse(course_id, seats_left) — The Course entity itself
  • Gateway to use case — An entity built from the row — The ORM row or a database cursor

The price is mapping code at each edge. Keep the DTOs small and named after the use case, and the mapping stays a few lines.

A runnable use case

The file below has one ring per block of code. The entity holds the rule, the use case orchestrates, the repository port is declared next to the use case, and an in-memory adapter stands in for the database. Nothing imports a framework.

The gateway stores plain dictionaries and rebuilds a Course on every get, so the storage format never reaches the use case. A PostgreSQL gateway would map rows the same way, and EnrollStudent would not change.

Clean and Hexagonal side by side

The Hexagonal Architecture topic covers ports, adapters and the Onion comparison in full. The short version: all three agree on the direction of dependencies. What differs is how much each says about the inside.

  • Structure inside the core — Not prescribed; only inside versus outside — Two rings: entities and use cases
  • Outgoing results to a UI — Not prescribed; Cockburn's example returns a value to the driving adapter — A presenter implements the use case's output port
  • Data at the boundary — Left to the designer — Request and response models; entities and rows never cross
  • Unit of the core — The application — One interactor per use case

Onion (Palermo, 2008) sits between them: rings around a domain model, with repository interfaces in the ring just outside it.

When it is overkill

The rings protect rules. With few rules, they mostly add files and mapping code.

  • Rules — Validation and CRUD — Real decisions: limits, pricing, state changes
  • Delivery — One API, one database — Several: API, jobs, queues, more than one store
  • Lifetime — Prototype or short-lived tool — Years, with tools likely to change
  • Team size — One or two people — Several teams touching the same core

A reasonable middle path keeps the entities and use cases as plain code with one repository port, and lets the controller return a response model directly instead of adding a presenter interface. Add rings and ports when a second driver or a second store actually appears.

Common mistakes

  • Anemic entities — Entities are bags of fields; every rule lives in use cases — Move rules that would hold on paper into the entity (Fowler's Anemic Domain Model)
  • ORM entities leak inward — Use cases receive and return ORM model classes with lazy loading and a session — Gateways map rows to entities; the use case never imports the ORM
  • Framework types in a use case — execute(request) takes the web framework's request object — The controller builds a request model first
  • Entities returned to the UI — The presenter reads entity fields directly — Return a response model built for that screen
  • Layers named by technology — Top-level folders controllers/, models/, services/ — Group by use case so the structure shows what the system does (Martin's Screaming Architecture)
  • A port for everything — Interfaces with one implementation and no test double — Add a port where a boundary or a fake is actually needed

What you will be able to do

  • State the Dependency Rule and name the four rings with an example of each.
  • Explain how a use case calls a presenter or a database without depending on it, using an output port.
  • Say what crosses a boundary (request and response models) and what never does (entities outward, ORM rows inward).
  • Write a use case with a repository port and an in-memory adapter, and test it with no framework.
  • Say how Clean differs from Hexagonal and when the rings are not worth their cost.

Interview questions

  • What is the Dependency Rule, and how does a use case send its result to a presenter without breaking it?
  • Why should DTOs cross the boundary instead of ORM entities?
  • What belongs in an entity and what belongs in a use case?
  • How do you test each ring?
  • When would you not use Clean Architecture, and how would you scale it down?

Sign up free to read the full topic

More Low-Level Design topics

Start free on Cariara · Coding problems · System design studio