SuperAPI AI Directory

develop-clean-architecture

Coding & development

View repository

Updated 10-16 08:49

About this skill

Clean Architecture — TypeScript

Apply Clean Architecture (Robert C. Martin), DDD tactical patterns, Hexagonal/Explicit Architecture (Herberto Graca), and Clean Code practices to TypeScript codebases. 76 rules across 11 categories.

Guardrails — never violate these

  • Never import outer layers from inner layers — domain imports nothing; application imports only domain
  • Never put ORM decorators or Prisma types on domain entities — use mappers in infrastructure
  • Never return null for domain errors — use a discriminated Result<T, E> return. In this repo the result branch uses ok: true | false; if E is a rich error union, give each error variant an _tag for exhaustive handling.
  • Never use as any or @ts-ignore without a documented justification comment
  • Never dispatch domain events before persistence — pull events AFTER save(), dispatch AFTER commit
  • Never share one type across domain/API/DB layers — accept cross-layer duplication, each model serves a different master
  • Never use barrel files (index.ts with export *) in application code — use direct imports
  • Never validate inside domain or use cases — parse at the adapter boundary with Zod, trust inside
  • Always use # private fields on entities (runtime encapsulation) — not the private keyword
  • Always separate Entity.create() (validates, emits events) from Entity.reconstitute() (loads from DB, no events)
  • Always wire dependencies in a single composition root (main.ts) — the only file that knows all concretions
  • Always use import type for cross-layer type imports — enable verbatimModuleSyntax: true
  • Always require strict: true + noUncheckedIndexedAccess + exactOptionalPropertyTypes in tsconfig

Trigger boundary

Use this skill when:

  • Designing new modules, services, or bounded contexts
  • Reviewing code for architectural or dependency-direction violations
  • Refactoring coupled systems toward cleaner layer separation
  • Implementing entities, value objects, aggregates, or domain events
  • Auditing tsconfig strictness, naming quality, or function design
  • Choosing between layered architecture and vertical slices

Do NOT use this skill when:

  • The task is purely about React/Vue/Angular component rendering (use framework skill)
  • The task is about build tooling, CI/CD, or deployment only
  • The task is a quick script or throwaway prototype with no long-term maintenance

Mode detection

Before starting, determine your mode:

Signal Mode Behavior
"Design", "architect", "structure", "plan" Designing Propose layer structure, reference decision-tables.md
"Review", "audit", "check", "assess" Reviewing Report findings with severity, never auto-fix, block on guardrail violations
"Implement", "write", "create", "add", "build" Implementing Write code following loaded references, verify with typecheck
"Refactor", "migrate", "extract", "move" Refactoring Apply minimal targeted changes, preserve behavior, verify tests pass
Ambiguous Ask Clarify with the user before proceeding

Required workflow

Step 1 — Classify the task

Identify the primary category and one adjacent category:

Category References to load When
Dependency Direction dep-inward-only, dep-interface-ownership, dep-dry-vs-duplication Import direction issues, layer coupling, DRY vs duplication
Entity Design entity-rich-not-anemic, entity-aggregate-roots, entity-create-reconstitute Domain modeling, invariants, aggregates, factories
Use Case Isolation usecase-orchestrates-not-implements, usecase-input-output-ports Use case design, port definitions, orchestration
Clean Code code-error-handling, code-parse-dont-validate, code-objects-vs-data Naming, functions, error handling, validation
TypeScript Strictness ts-strict-config, ts-branded-types, ts-result-type, ts-lsp-performance Type safety, config, branded types, LSP speed
Architecture Patterns pattern-cqrs-separation, pattern-domain-events, pattern-vertical-slices CQRS, events, vertical slices
Boundaries bound-composition-root, adapt-explicit-architecture, adapt-controller-thin Composition root, ports, adapters, controllers
Framework Isolation frame-domain-purity, frame-orm-in-infrastructure ORM leaks, framework coupling
Testing test-testing-pyramid, test-layer-isolation Test strategy, pyramid alignment

Steering note: Most tasks span two categories. Load the primary reference plus one adjacent. If uncertain, scan the category table for keywords matching the user's request.

Step 2 — Load references

Read the reference file(s) from references/ identified in Step 1. Read the full file — do not skim. If a loaded reference example conflicts with the guardrails or repo conventions in this file, follow this file.

If the task involves existing code, also read:

  • The project's tsconfig.json (compare against ts-strict-config.md)
  • The project's layer structure (identify domain/, application/, infrastructure/ or equivalents)

Steering note: Always check if the project already has an AGENTS.md or architecture docs. Adapt your output to match the project's existing naming conventions and layer structure.

Step 3 — Execute the task

Apply patterns from loaded references. Follow mode-specific behavior:

In designing mode:

  • Propose folder structure per comp-screaming-architecture.md (package-by-component)
  • Define port interfaces in application/domain layer, implementations in infrastructure
  • Load decision-tables.md for architecture selection based on domain complexity

In implementing mode:

  • Entities: # private fields, create() + reconstitute() factories, pullDomainEvents()
  • Use cases: constructor-injected ports, Result<T,E> returns, orchestrate-not-implement
  • Adapters: thin controllers (parse, delegate, respond), Zod schemas at boundary
  • import type for all type-only imports across layers

In reviewing mode:

  • Check every guardrail (top of this file) — violations are automatically CRITICAL
  • Check dependency direction: inner layers must never import from outer layers
  • Check entity design: no anemic models, no ORM decorators, proper factories
  • Flag severity: CRITICAL (guardrail), WARNING (quality degradation), INFO (polish)

In refactoring mode:

  • Apply minimal changes. One refactoring at a time.
  • Preserve public API contracts. Preserve test behavior.
  • Load decision-tables.md for anti-pattern recognition table
  • For low-complexity requests, the smallest acceptable boundary split is: pure domain logic, one application entry point, boundary parsing in adapters, and one composition root.

Steering note: Never apply Clean Architecture to a simple CRUD app that doesn't need it. Check domain complexity first. For LOW complexity, vertical slices + Zod + Result types are sufficient. If the user explicitly asks for a refactor toward cleaner boundaries anyway, do the smallest useful version: isolate pure domain logic, keep one composition root, and move boundary parsing to adapters without forcing extra ceremony.

Step 4 — Verify

After making changes:

  1. Read the project's scripts or workspace docs first, then run the strongest project-native typecheck command available (npm run typecheck, pnpm typecheck, tsc --noEmit -p tsconfig.json, or npx tsc --noEmit only if the repo installs TypeScript locally and that command works here)
  2. Run tests if configured (npm test, pnpm test, or project equivalent). If no tests are configured, state that explicitly and fall back to build + typecheck instead of pretending a test step exists.
  3. Check imports: no outer-to-inner violations
  4. Check entities: # private fields, create/reconstitute separation
  5. Check boundaries: ports in consuming layer, implementations in infrastructure

If the project contains TSX but the task is architecture-only, either install the required React runtime/types before typechecking or scope the verification command to the non-UI packages/modules you actually changed. State which path you took.

Step 5 — Deliver

  • Designing: Output folder structure, port interfaces, layer diagram
  • Implementing: Output complete, compilable code with explicit return types
  • Reviewing: Output structured findings list with severity, file, line references
  • Refactoring: Output targeted diffs with before/after

Steering note: Always produce a deliverable — code, findings list, or structure. Never end with only commentary.

Common mistakes to avoid

Mistake Why it's wrong What to do instead
Applying full Clean Architecture to a simple CRUD app Over-engineering; 4+ layers for no benefit Use vertical slices; add layers only when complexity emerges
Sharing one type across all layers ("DRY") DB schema change breaks API; passwordHash leaks Per-layer models with mappers — each has different change reason
Mocking domain entities in tests Entities are pure — mocking defeats the purpose Test entities directly; mock only ports in use case tests
Dispatching events before commit Event published, DB rolled back = inconsistency Pull events after save, dispatch after commit
Using private keyword on entity fields Compile-time only — bypassable with as any Use # private fields for runtime encapsulation
Putting Zod validation inside domain/use cases Wrong layer — parsing belongs at the adapter boundary Parse at HTTP boundary with Zod; domain receives trusted types
Using barrel index.ts in app code Cascade loads 3x+ modules, causes circular deps Direct imports from source files
One constructor for both creation and DB loading Duplicate event emission on every load; or skipped validation create() for new entities, reconstitute() for DB loads
Flagging absence of patterns as violations Optional patterns (events, CQRS) are not mandatory Only audit what exists — don't flag absence of optional patterns

Reference routing

All references live in references/. Load by category:

Prefix Category Count Key files
dep- Dependency Direction 7 dep-inward-only, dep-interface-ownership, dep-dry-vs-duplication, dep-acyclic-dependencies, dep-data-crossing-boundaries, dep-no-framework-imports, dep-stable-abstractions
entity- Entity Design 8 entity-rich-not-anemic, entity-aggregate-roots, entity-create-reconstitute, entity-domain-services, entity-encapsulate-invariants, entity-no-persistence-awareness, entity-pure-business-rules, entity-value-objects
usecase- Use Case Isolation 6 usecase-orchestrates-not-implements, usecase-input-output-ports, usecase-explicit-dependencies, usecase-no-presentation-logic, usecase-single-responsibility, usecase-transaction-boundary
code- Clean Code 11 code-error-handling, code-parse-dont-validate, code-objects-vs-data, code-comments-discipline, code-composition-over-inheritance, code-flag-arguments, code-function-arguments, code-immutability, code-meaningful-names, code-no-side-effects, code-small-functions
comp- Component Cohesion 6 comp-screaming-architecture, comp-barrel-file-discipline, comp-common-closure, comp-common-reuse, comp-reuse-release-equivalence, comp-stable-dependencies
ts- TypeScript Strictness 11 ts-strict-config, ts-branded-types, ts-result-type, ts-lsp-performance, ts-boundary-enforcement, ts-conditional-types, ts-discriminated-unions, ts-module-structure, ts-phantom-types, ts-satisfies-operator, ts-verbatim-module-syntax
pattern- Architecture Patterns 4 pattern-cqrs-separation, pattern-domain-events, pattern-vertical-slices, pattern-repository-ts
bound- Boundary Definition 7 bound-composition-root, bound-main-component, bound-boundary-cost-awareness, bound-defer-decisions, bound-humble-object, bound-partial-boundaries, bound-service-internal-architecture
adapt- Interface Adapters 6 adapt-controller-thin, adapt-explicit-architecture, adapt-anti-corruption-layer, adapt-gateway-abstraction, adapt-mapper-translation, adapt-presenter-formats
frame- Framework Isolation 5 frame-domain-purity, frame-orm-in-infrastructure, frame-di-container-edge, frame-logging-abstraction, frame-web-in-infrastructure
test- Testing Architecture 5 test-testing-pyramid, test-layer-isolation, test-boundary-verification, test-testable-design, test-tests-are-architecture
— Decision Tables 1 decision-tables (architecture selector, anti-patterns, conflicts, Do/Don't)

Additional supporting files:

Guardrails — repeated for recall

  • Source dependencies point inward only — domain never imports outer layers
  • Entities use # private fields, create() + reconstitute() factories, Result<T,E> returns using the repo's discriminated union convention
  • Parse at boundary with Zod — never validate inside domain
  • Events dispatched AFTER persistence — never before commit
  • Accept cross-layer DTO duplication — DRY ends at the layer boundary
  • main.ts is the only composition root — the only file that knows all concretions