Clean Architecture

The dependency rule: source-code dependencies point inward toward higher-level policy. Robert Martin, 2017. Framework-neutral, testable, durable.

Source code dependencies point inward. The domain doesn’t know about the database. The database doesn’t know about the framework. Tests run without booting either. Status: STUB — promoted to OUTLINE in Y1 Phase 5.

What this pattern is

Clean Architecture, codified by Robert Martin in 2017, expresses the same insight from a different angle than DDD: source-code dependencies should point inward from concrete-and-volatile (frameworks, databases, UI) toward abstract-and-stable (business rules, entities, use cases). The center is the domain, pure logic that knows nothing about the world. Around it sit use cases (application-specific behavior). Around those, interface adapters (controllers, presenters, gateways). Outside that, frameworks and drivers.

The dependency rule is the load-bearing invariant: nothing in an inner layer may know about an outer layer. The domain doesn’t import anything from the database. The use cases don’t import anything from HTTP frameworks. The interface adapters know about both, translating between them. Concentric rings of decreasing stability, with dependency arrows always pointing inward.

The payoff is testability and longevity. The domain can be tested without booting Postgres, without standing up Flask, without rendering React. When the database changes (Mongo → Postgres → DynamoDB), only the outer adapter layer changes. When the framework changes (Flask → FastAPI → Sanic), the domain doesn’t notice. When the UI shifts (server-rendered → SPA → mobile-first), the use cases and entities are unaffected. A ten-year-old codebase can survive multiple full-stack rewrites of its outer layers because the inner layers never had to change.

Concrete instances in the wild

  • Well-organized Django services. Django with a services layer between views and models. Views call service functions; services call domain logic; domain logic doesn’t know about HTTP or ORM specifics.
  • Spring Boot hexagonal. Spring services where interfaces live in the domain package and implementations live in the infrastructure package. Dependency injection wires them together.
  • Go services with layered packages. internal/domain, internal/usecases, internal/handlers, internal/repositories. Import direction is enforced by convention plus review.
  • CQRS applications. Command and Query Responsibility Segregation naturally aligns with clean architecture. Commands and queries are use cases; their handlers depend on domain entities and infrastructure ports.
  • Onion architecture. Same pattern, different metaphor. Concentric onion layers with dependency pointing inward.
  • Node/TypeScript services with dependency-injection frameworks. NestJS or InversifyJS with clean layered modules. The framework enforces dependency direction structurally.
  • Rust workspaces with domain crates. Crate structure enforces the dependency rule at the compiler level; the domain crate has no dependency on the infrastructure crate.
  • Legacy Java rewrites. Twelve-year-old codebases that survived Struts → JSF → Spring MVC → Spring Boot transitions because the domain layer was never coupled to any of them.

Why this pattern matters

Without the dependency rule, framework churn destroys codebases. In 2010 you built on Django. In 2015 you rewrote on Rails. In 2020 you migrated to FastAPI. In 2025 you rebuilt on Bun/Elysia. Every migration was 6-12 months of engineering time, and the business logic — the reason the company exists — got rewritten every time, incorrectly, with different bugs each iteration.

Clean architecture insulates the business logic from the churn. The framework you chose in 2010 wraps the same domain code that will still be running in 2030. The domain isn’t fragile; it’s the most stable part of the system because it depends on nothing external. When the outer layers rot, you rewrite them one at a time, and the domain keeps producing correct business behavior throughout.

The pattern also enforces testability. Domain code that depends on the database can only be tested with a database. Domain code that depends only on abstract interfaces can be tested with fakes at nanosecond speeds. A test suite that runs in 200ms because it doesn’t touch I/O is a very different engineering experience from a test suite that runs in 8 minutes because every test spins up Postgres and Redis. The former lets you refactor with confidence; the latter makes you avoid changes.

Depth progression

STUB     ← you are here.
OUTLINE  Promoted when Y1 Phase 5 walks the dependency rule explicitly.
DEEP     Promoted when a Y2 service applies the dependency rule AND you've
         done a real swap (e.g., swapping a database, a framework, or a
         third-party SDK) without rewriting the domain.

Preview: what OUTLINE will answer

When Y1 Phase 5 promotes this entry to OUTLINE, it will name:

  • PROBLEM. How do you keep business logic stable across framework, database, and infrastructure changes?
  • PRINCIPLES. Dependencies point inward from volatile to stable. Domain layer has no external dependencies. Use cases orchestrate domain logic without knowing about I/O. Interface adapters translate between domain and infrastructure. Outer layers are replaceable.
  • TRADE-OFFS. Layer count and strictness (few layers = coupled, many layers = ceremony). Interface indirection cost vs testability. Framework-idiomatic code vs framework-neutral code. Language features that support the pattern (interfaces in Go, traits in Rust, protocols in Swift) vs languages that don’t (JavaScript before TypeScript).
  • TOOLS (time-stamped as of 2026-06): Uncle Bob’s canonical concentric-circle diagram, layered project structures (in Java: com.company.domain / .usecases / .adapters / .infrastructure), dependency-injection frameworks (Spring, NestJS, Dagger, Wire), and language-level import controls (Go’s internal packages, Rust’s crate boundaries).

The DEEP promotion, after Y2 with a real swap experience, will add MASTERY (operating a clean-architecture service through changes), COMPARE (Clean Architecture vs Hexagonal vs Onion — three names for near-identical patterns), OPERATE (real friction from over-applied or under-applied layering), and CONTRIBUTE (a documented refactor toward or away from clean architecture, with the reasoning in an ADR).

Canonical references

  • Robert C. Martin, Clean Architecture: A Craftsman’s Guide to Software Structure and Design (2017) — the canonical text. Somewhat opinionated but foundational.
  • Uncle Bob’s Clean Architecture blog post (2012) — the original short article. Free.
  • Herberto Graça’s writing at herbertograca.com — deep comparisons of Clean Architecture, Hexagonal, Onion, and Ports and Adapters. Free, high signal.
  • Jason Taylor’s C# Clean Architecture template on GitHub — concrete reference implementation in a mainstream language.
  • Vaughn Vernon’s Implementing Domain-Driven Design — has extensive material on where Clean Architecture and DDD converge.

Cross-references