Clean Architecture
01 / 02

The Layers & the Dependency Rule

The Layers & the Dependency Rule

The Goal

Organize code so business logic (Entities, Use Cases) is independent of frameworks, databases, and UI — making the core easier to test and change without being tied to any specific technology. Related architectures (Hexagonal / Ports and Adapters, Onion) converge on the same core idea.

The Dependency Rule

Source-code dependencies point only INWARD. Outer layers (frameworks, UI, DB) may depend on inner layers (business logic); inner layers must never depend on outer ones. This is Dependency Inversion Principle applied at architectural scale — Clean Architecture is SOLID applied to components, not just classes.

The Four Rings

Entities (innermost) — enterprise-wide business rules, least likely to change. Use Cases — application-specific rules orchestrating Entities toward a specific goal (e.g. "transfer funds"). Interface Adapters — Controllers/Presenters translating between the outer world and Use Cases' expected format. Frameworks & Drivers (outermost) — the web framework, DB driver, UI toolkit; the most volatile, easiest to swap.

Gateways / Repository Interfaces

// use-cases layer — defines the abstraction it needs
interface UserRepository {
  findById(id: string): Promise<User | null>;
  save(user: User): Promise<void>;
}

// frameworks/drivers layer — implements it, depends inward on the interface
class PostgresUserRepository implements UserRepository {
  async findById(id: string) {
    const row = await db.query('SELECT * FROM users WHERE id = $1', [id]);
    return row ? User.fromRow(row) : null;  // maps DB row -> domain Entity
  }
  async save(user: User) { /* ... */ }
}

Boundaries

Every crossing point between layers is a boundary where data flows but the Dependency Rule must be respected — typically enforced via an interface defined on the inner side, implemented on the outer side.

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free