Clean Architecture
Structure systems so that business logic is independent of frameworks.
50 lessons
ADP: The Acyclic Dependencies Principle
No cycles allowed in the component dependency graph — cycles cause the morning-after syndrome and prevent isolated builds and tests.
Architectural Boundaries: Drawing Lines
Architecture draws lines between what matters (business rules) and what doesn't (technical details) — these lines protect core business logic from changes in external tools.
What is Architecture? Lifecycle Support
Architecture's primary purpose is to support the system's lifecycle — development, deployment, operation, and maintenance — to maximize productivity and minimize lifetime cost.
Anatomy of a Boundary: Crossing Boundaries
When crossing an architectural boundary, source code dependencies must point opposite to the flow of control — polymorphism enforces this regardless of who calls whom.
Business Rules and Entities
Critical Business Rules would exist even without a computer — Entities encapsulate these rules and data, forming the most stable, UI-and-persistence-agnostic heart of the system.
Clean Embedded Architecture: Avoiding Firmware
Software doesn't wear out, but hardware becomes obsolete — a Hardware Abstraction Layer prevents business logic from becoming firmware trapped on a specific processor.
The Component Tension Diagram
REP, CCP, and CRP pull in opposite directions — architects must balance these tensions as the project evolves from development focus to reuse focus.
Components: The Unit of Deployment
Components are the smallest deployable units — JARs, DLLs, Gems — the building blocks of a plugin architecture where modules connect at runtime.
Component Cohesion: The Common Reuse Principle
Don't force users of a component to depend on things they don't need — depending on a component means depending on everything it contains.
Decoupling Modes: Source, Deployment, and Service
Decoupling can happen at source code, deployment, or service level — good architecture lets you start as a monolith and evolve into services if boundaries are well-defined.
The Dependency Rule in Clean Architecture
🔒Source code dependencies can only point inward — nothing in an inner circle can know anything about an outer circle, keeping the business core stable and reusable.
The Details: Database, Web, and Frameworks
🔒The database, web layer, and frameworks are delivery mechanisms — a good architect postpones these decisions and keeps business rules independent to avoid a costly technology marriage.
Device Independence
🔒Tying business logic to specific I/O devices is a costly mistake — architecture should be agnostic to whether data comes from cards, terminals, files, or APIs.
DIP: The Dependency Inversion Principle
🔒Source code dependencies should point only to abstractions — Abstract Factories create the architectural boundary between volatile concrete code and stable policy.
Dependency Inversion: Control Over Dependencies
🔒With interfaces, architects make source-code dependencies point against the control flow, granting absolute power over module coupling.
The Eisenhower Matrix in Development
🔒Architecture is important but rarely urgent. Developers must defend good structure against short-term business pressure.
Architecture Layers: Entities and Use Cases
🔒Entities hold critical business rules that exist across the enterprise; Use Cases orchestrate application-specific flows and are isolated from UI and database changes.
Functional Programming: The Value of Immutability
🔒Functional programming disciplines assignment. Immutability eliminates race conditions, deadlocks, and concurrent update issues at the architectural level.
The Goal of Architecture and Design
🔒Design and architecture are the same thing — their true measure is the effort required to meet client needs while minimizing lifetime cost.
The Humble Object Pattern
🔒Split hard-to-test from easy-to-test behavior — the Presenter prepares all logic into a testable ViewModel; the View is a humble object that only renders what it receives.
The Devil Is in the Implementation Details
🔒The best architecture fails without disciplined use of access modifiers — the compiler is your strongest ally in enforcing Clean Architecture rules across the team.
Interface Adapters and Data Passing
🔒Interface Adapters convert data between the format use cases understand and the format external systems need — boundaries are always crossed with simple DTOs, never raw entities or DB rows.
ISP: The Interface Segregation Principle
🔒Don't depend on things you don't use — fat interfaces force unnecessary recompilation, coupling, and vulnerability to failures in unrelated code.
Keep Options Open
🔒A good architect maximizes the number of decisions not yet made — separating policy from details so database and framework choices can be postponed until more is known.
Layer Decoupling
🔒Separate what changes at different rates — UI, application business rules, domain business rules, and database each change for different reasons and should be horizontal layers.
LSP: The Liskov Substitution Principle
🔒Subtypes must be substitutable for their base types — LSP violations at the architectural level force expensive special-case logic into the design.
The Main Component: The Ultimate Detail
🔒Main is the dirtiest, lowest-level component — the entry point that wires all dependencies and hands control to the clean architecture above it.
The Main Sequence: The A/I Balance
🔒Components should balance Abstraction (A) and Instability (I) — the Pain Zone (stable+concrete) and Uselessness Zone (abstract+unstable) are architectural anti-patterns to avoid.
OCP: The Open/Closed Principle
🔒Software artifacts should be open for extension but closed for modification — adding new behavior should add code, not change existing code.
OOP: The Reality of Encapsulation
🔒OOP didn't invent encapsulation — C had perfect encapsulation before C++ and Java actually weakened it with header file requirements.
Package by Component: The Hybrid Approach
🔒Group all responsibility for a clean feature boundary into one package — internal persistence and logic stay private, the compiler enforces what's visible to the rest of the system.
Organizational Strategies: Package by Feature
🔒Group code by domain feature — top-level packages reveal the business, every feature is co-located, and searchability dramatically improves as the system grows.
Organizational Strategies: Package by Layer
🔒The simplest code organization groups by technical layer (web/service/repository) — easy to start but hides business intent and allows accidental cross-layer shortcuts.
Partial Boundaries: Anticipation Strategies
🔒Full architectural boundaries are expensive — partial boundaries using Strategy or Facade patterns preserve future separation points without the full upfront cost.
The Peripheral Anti-Pattern
🔒The peripheral anti-pattern occurs when infrastructure code talks directly to other infrastructure code, bypassing the domain — like the Paris ring road, traffic flows around the city instead of through it.
Policy and Level
🔒Level is the distance from inputs and outputs — the farther from I/O, the higher the level. Source code dependencies should always point toward the highest-level, most stable policies.
Polymorphism: The Power of Plugins
🔒Polymorphism lets you treat external dependencies — database, UI, I/O — as interchangeable plugins, making core logic device-independent.
Ports and Adapters (Hexagonal Architecture)
🔒The inside is the domain; the outside is infrastructure — outside depends on inside, ports speak the domain's language, and adapters translate between domain and external systems.
Programming as Science: Falsifiability and Testing
🔒Tests don't prove correctness — they prove absence of known incorrectness. Good architecture produces easily falsifiable modules.
Programming Paradigms: Discipline, Not Tools
🔒The three paradigms — structured, object-oriented, and functional — impose discipline by removing capabilities, not adding them.
Component Cohesion: REP and CCP
🔒REP groups classes released together; CCP groups classes that change together for the same reason — both mirror SRP at the component scale.
SAP: The Stable Abstractions Principle
🔒A component should be as abstract as it is stable — the most stable components should be pure interfaces so they can be extended without modification.
Screaming Architecture
🔒Your folder structure should reveal what the system does, not what framework it uses — a health system screams 'Health', not 'Rails'.
SDP: The Stable Dependencies Principle
🔒Depend in the direction of stability — a volatile component should never be depended upon by a stable one, or that stability is permanently compromised.
Services and Microservices: Are They Architecture?
🔒Services don't define architecture — a service with poor internal structure is just an expensive function call. True architectural boundaries can exist inside a monolith or across services.
SRP: The Single Responsibility Principle
🔒A module should be responsible to only one actor — preventing changes for one department from accidentally breaking another.
Structured Programming: Discipline over Direct Control
🔒Dijkstra proved unrestricted goto is harmful. Sequence, selection, and iteration alone are sufficient for correct, decomposable modules.
The Test Boundary
🔒Tests are the outermost architectural circle — fragile tests that couple to implementation details make the system rigid; a Test API decouples test structure from application structure.
The Two Values of Software: Behavior vs Structure
🔒Software derives value from behavior and structure. A perfectly working but rigid system will fail when requirements change.
Use Case Decoupling
🔒Use cases are vertical slices that cut through all layers — decoupling them lets each evolve independently without one use case's changes colliding with another's.