# Clean Architecture

> Apply Robert C. Martin's Clean Architecture principles (SOLID at the module level, component cohesion/coupling, the Dependency Rule, boundaries, the Humble Object pattern) whenever planning a new system/feature or reviewing an existing one for structural health. Trigger proactively any time the user is designing, structuring, architecting, building, or refactoring software — not just when they say "clean architecture." Covers: sketching a new tool before writing code, splitting a single-file HTML app into sections, organizing a Flask/SQLite project into modules, reviewing code for tangled dependencies or mixed responsibilities, or judging whether a design doc's structure will hold up as the system grows. Also use for "how should I structure this," "does this design make sense," "is this over-engineered," or when a file/spec is shared for a general structural check — even without the words "clean architecture" appearing.

- Skill: `msg-01/clean-architecture` (Agent Skill, multi-file: 6 files)
- Install (CLI): `npx skillmds@latest add msg-01/clean-architecture`
- Raw SKILL.md: https://api.skillmd.com/api/skills/msg-01/clean-architecture/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: msg-01 (https://skillmd.com/u/msg-01)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/msg-01/clean-architecture

---


# Clean Architecture Advisor

## What this is for

Bringing Robert C. Martin's *Clean Architecture* to bear on real, often small-scale work — a single-file HTML tool, a Flask/SQLite desktop app, a multi-document technical spec — in two modes:

- **Planning** — before or while building something new, shape it so the parts that change for different reasons stay separate, and the parts likely to survive rewrites (business rules) don't end up depending on the parts likely to get replaced (frameworks, storage, UI).
- **Reviewing** — looking at something that already exists and honestly assessing whether its structure will hold up, using the book's actual vocabulary (Dependency Rule, SRP-by-actor, coupling health, boundary hygiene) rather than vague "this feels messy" impressions.

The five reference files in `references/` hold the full depth on each principle. This file is the cheat sheet and the workflow — open a reference file when a specific principle needs its full reasoning or worked examples, not for every pass.

## Core stance: rigor scales to the project, not the other way around

The book's own running tension (see `references/component-cohesion-coupling.md`, §2.4) is that its own principles pull in different directions — some toward more separation, some toward less — and there is **no permanently correct boundary**, only the right one for how a system has actually evolved so far. That cuts both ways:

- Applying the full four-ring layering (Entities / Use Cases / Interface Adapters / Frameworks) to a single-file HTML tool is itself a design smell the book would flag — effort spent on separation nobody needs yet is the same mistake as an abstract interface nobody implements (the "Zone of Uselessness" idea, generalized past just code).
- But even a single file benefits from the underlying rule the layers exist to serve: **keep the parts that change for different reasons apart, and don't let the part that's actually hard to change (the business logic) end up depending on the part that's easy to change (which JS timer API, which DOM structure, which UI framework).**

So the real job in both planning and reviewing is judgment about *how much* structure a specific piece of work has earned — not maximizing separation, and not skipping it either. When in doubt, say so plainly rather than defaulting to either extreme.

## The condensed mental model

### The Dependency Rule (the one rule everything else serves)
Source code dependencies point in only one direction: **toward** the code least likely to change for incidental reasons (business rules), **away from** the code most likely to change for incidental reasons (frameworks, databases, UI, third-party SDKs). Concretely: a function implementing an actual business/support rule should never import, call, or know the shape of a database row, a framework request object, or a specific rendering library — if it needs something from that direction, it should depend on an interface *it* defines, and let the detail-side code implement that interface. Full depth: `references/dependency-rule-and-layers.md`.

### SOLID, reframed at the module level
| Principle | One-line rule | Its component-level echo |
|---|---|---|
| **S**RP | A module answers to one **actor** (one reason to change), not "does one thing" | Common Closure Principle |
| **O**CP | Adding a feature shouldn't require editing large volumes of working code | — |
| **L**SP | Anything swappable behind a shared interface must actually honor that interface's contract, not just its method signatures | — |
| **I**SP | Don't force a caller to depend on parts of an interface/module it never uses | Common Reuse Principle |
| **D**IP | Depend on abstractions you control, not on volatile concrete things | Stable Abstractions Principle |

The most common misreading worth actively guarding against: SRP is not "small functions/classes." It's "one actor." A 30-line function serving one stakeholder is SRP-clean; a 10-line function quietly serving two is not. Full depth: `references/solid-principles.md`.

### Component cohesion & coupling (once there's more than one file/module)
| Principle | Pulls toward | Violation looks like |
|---|---|---|
| REP | More, smaller, versioned components | An unversioned "shared utils" thing other code depends on that changes without warning |
| CCP | Fewer, bigger components (changes stay local) | One feature change requires touching five separate files/modules every time |
| CRP | More, smaller components | A consumer needing one function drags in nine unrelated ones and gets redeployed on their changes too |
| ADP | No cycles, ever | A depends on B, B depends on A (directly or through a chain) |
| SDP | Depend toward stability | Core business logic imports a wrapper around a third-party SDK that changes on the vendor's schedule |
| SAP | Stable things should be abstract, not concrete | A "core" module everything depends on, with no interfaces to extend through — rigid, not just stable |

Metrics worth actually computing on a real review, not just eyeballing: `I = Ce/(Ca+Ce)` (instability), `A = abstract/total` (abstractness), `D = |A+I-1|` (distance from healthy). Full depth incl. the Main Sequence diagram: `references/component-cohesion-coupling.md`.

### Boundaries & the Humble Object
A boundary is worth drawing where two things change for different reasons *and* the cost of separating them is less than the cost of leaving them tangled — not automatically everywhere. Where IO, rendering, or a framework call is unavoidably hard to test, split it: a **humble** half that's just the raw framework/DOM/socket call (deliberately dumb, not unit-tested), and a **testable** half holding everything that can reasonably be verified without that framework/DOM/socket present. Full depth: `references/boundaries-and-humble-object.md`.

### The planning workflow the book itself demonstrates
Chapter 33's worked example (video sales) goes: identify actors → identify use cases per actor → draw component/boundary architecture → wire dependencies so `main` is the one place allowed to know about everything. Worth mirroring even for small tools, scaled down. Full depth: `references/case-study-and-implementation.md`.

## Workflow A — Planning something new

1. **List the actors.** Even for a solo tool, actors are real: e.g. "the support agent using this during a live case" vs. "whoever reviews AHT metrics later" vs. "the timer engine's own internal bookkeeping." Two actors sharing one function is next round's bug.
2. **List use cases per actor**, and note which parts are actual business/support policy (the part that would stay true even if you rewrote the UI from scratch) vs. which parts are detail (which timer API, which storage format, which CSS).
3. **Decide what needs a real boundary vs. what doesn't yet.** Ask directly: what's the cost of separating this now vs. the cost of it staying tangled until it actually needs to change? If the honest answer is "nothing forces this yet," a **partial boundary** (define the interface, keep everything in one file/module for now) is often the right level — full separation is not always the more disciplined choice.
4. **Point dependencies at policy.** Wherever the natural build order would have business logic depend on a detail (e.g., "the exporter needs to know the case object's exact shape from local storage"), invert it: define the shape the business logic wants, and have the storage-reading code conform to that.
5. **Wrap unavoidable IO/DOM/framework calls in a Humble Object** so the logic around them stays testable even if the wrapper itself isn't.
6. **Sanity-check before calling it done:** is anything stable (rarely-changing) depending on anything volatile (frequently-changing)? Is there a "core" piece so concrete that extending it later means editing it instead of adding to it?

## Workflow B — Reviewing something that exists

Borrow the honesty discipline from careful technical review generally: verify against the actual file, don't take a comment or prior claim at face value, and don't manufacture a finding just to have something to say. Specifically, check for:

1. **Dependency direction violations** — trace real calls/imports, not just file organization. Does any business-logic function reach directly into a DB row shape, a framework's request/response object, or a specific rendering call? That's the Dependency Rule broken, regardless of what the folder structure suggests.
2. **Actor entanglement (SRP)** — is there a function/section serving two stakeholders whose reasons to change are genuinely unrelated? Ask what would break if only one of them got what they wanted changed.
3. **Detail leaking upward** — a database's row structure, a third-party SDK's response type, or a UI framework's object passed into and used by code that's supposed to be policy. This is the single most common real bug source the book warns about, and it's concretely checkable: find the exact line where the outer-layer shape crosses into inner-layer code.
4. **Coupling health, once there's more than one module/file** — any dependency cycles (ADP)? Anything stable depending on something volatile (SDP)? A stable piece with no abstraction to extend through, forcing edits instead of additions (SAP — "Zone of Pain")?
5. **Boundary hygiene** — a half-finished boundary (an interface defined, but concrete details leaking through it anyway) is worse than no boundary, because it looks safe and isn't. On the other side, a fully-built boundary with only one implementation and no real reason to expect a second one is effort spent on a problem that doesn't exist yet.
6. **Humble Object check** — is genuinely untestable glue (raw `setInterval`/`fetch`/DOM manipulation) mixed directly into code that has real logic in it, making the logic itself hard to verify?

## Writing up findings

Lead with the verdict — real Dependency Rule violation, vs. a stylistic preference, vs. a pragmatic and currently-fine shortcut for the project's actual size — not a flat list. Show the concrete fix (the actual interface, the actual moved function), not just a description of the problem, so the reasoning can be checked independently. If a pass turns up nothing beyond acceptable shortcuts for the project's current scale, say that plainly instead of inflating minor style preferences into "violations" — a clean bill of health is a legitimate outcome, not a sign the review wasn't thorough enough.

## Reference files

| File | Open it when... |
|---|---|
| `references/solid-principles.md` | Reviewing at the class/function/module level — SRP-by-actor, OCP's protection hierarchy, LSP beyond inheritance, ISP, DIP and Abstract Factories |
| `references/component-cohesion-coupling.md` | More than one file/module/component is involved — cohesion (REP/CCP/CRP), coupling (ADP/SDP/SAP), the I/A/D metrics |
| `references/dependency-rule-and-layers.md` | Establishing or checking the core Dependency Rule, the four-ring model, what data is allowed to cross a boundary |
| `references/boundaries-and-humble-object.md` | Deciding whether/how to draw a boundary, full vs. partial boundaries, applying the Humble Object pattern |
| `references/case-study-and-implementation.md` | Planning a new system end-to-end, or wanting real-world examples of how different languages/repos enforce these rules in practice |

