clean-with-bob
Clean with Bob
Audit and plan migration toward Uncle Bob's classical Clean Architecture, in any language, with a layer-first folder hierarchy (no feature slicing, no shared kernel) and framework- and third-party-agnostic business logic. This skill only audits and plans — never writes production code. State is persisted so a session can resume mid-workflow. Code examples are mostly TypeScript for concreteness; rules apply to every supported language (per-language idioms in OOP-FP.md). This skill is self-contained — every rule, threshold, layer definition, and audit checklist it needs lives inside this directory. It does not invoke or depend on any other skill.
Core rules
- Four concentric layers, in this exact taxonomy. Entities (enterprise business rules) → Use Cases (application business rules) → Adapters (controllers, presenters, gateways) → Infrastructure (web, DB, external interfaces). These are the working folder names this skill emits; Uncle Bob's textbook names (
Interface Adapters,Frameworks & Drivers) are retained as historical/textbook cross-reference only — see STRUCTURE.md §Bob-canonical names. See LAYERS.md. - The Dependency Rule. Source code dependencies point inward only. An outer layer may import inner ones; an inner layer must never name an outer one. Cross this rule and the rest of the architecture is theatre.
- Layer-first folders, no feature slicing. One global hierarchy per project:
<src>/entities/,<src>/usecases/,<src>/adapters/,<src>/infrastructure/, plus the composition root at the apex (<src>/main.<ext>). Files inside each layer can be grouped by domain concept (entities/order/,usecases/billing/) but the layer is the top-level partition, not the feature. See STRUCTURE.md. - No shared kernel. Entities are the shared layer. Anything reused across use cases lives in
entities/if it's a domain concept (cross-entity value objects go inentities/values/), or inadapters/(e.g. a generic gateway) if it's an integration concept. Don't introduceshared/,common/, orcore/folders. - Frameworks live only in Infrastructure (canonical: Frameworks & Drivers). Any web framework, ORM, mailer, queue client, or HTTP-server import found in entities or use cases is a violation. Adapters reference framework types only as needed to translate at the boundary (e.g. an HTTP controller takes a
Requesttype) — they hold no framework configuration. - Third parties cross the boundary at gateways. ORMs, payment SDKs, HTTP clients live in
adapters/gateways/(translation) andinfrastructure/(configuration & instantiation). Never in entities or use cases. - Classes allowed; OOP discipline depends on the language. OOP-native languages (Java, Kotlin, C#, Swift, Ruby, PHP) use idiomatic classes — concrete final classes (or sealed interfaces / sealed classes for sum-typed entities and state machines) for entities, interfaces for ports, concrete classes for adapters. FP-leaning languages (JS/TS, Python, Go, Rust, Elixir) favour functions + closures + records, even though their syntax permits classes. See OOP-FP.md.
- Errors are values in entities and use cases when the language supports it ergonomically (
Result<T, E>,Either,(T, error),OneOf<T, E>, sum-type return). Throwing/raising is reserved for exceptional bugs, never for control flow. OOP-native languages may use checked exceptions at the use-case boundary if that is the platform norm — see OOP-FP.md for the per-language stance.
Full vocabulary in LANGUAGE.md. Use these terms exactly — don't drift into "service," "module," "boundary," "feature," or "slice." This skill is not about feature slices.