NestJS Architecture and Principles
Make architecture decisions from the repository's actual constraints. Prefer the least complex design that protects the required boundaries and can evolve safely.
Start with evidence
Before recommending or changing architecture:
- Read
package.json,nest-cli.json, TypeScript configuration, bootstrap files, and the relevant modules. - Map the module import graph, provider exports, entry points, persistence ownership, transports, and existing tests.
- Identify the business capability and the concrete change pressure: team ownership, independent scaling, volatile integrations, transaction boundaries, security, or delivery speed.
- State the current architecture in one sentence. Do not label a project "Clean Architecture" or "DDD" from folder names alone.
- Preserve healthy local conventions. Flag a convention only when it damages correctness, security, operability, or an explicit contract.
If the task is version-sensitive, verify the installed NestJS version and current official documentation before using an API or command.
Choose the architecture level
Use the architecture ladder in references/architecture-ladder.md.
Default direction:
- Start with a modular monolith organized by business capability.
- Add internal layers when domain logic or infrastructure volatility makes the boundary valuable.
- Add ports where the application needs to isolate a replaceable external concern.
- Add CQRS only when command and query behavior genuinely diverge.
- Split a service only when it has an independent deployment, ownership, scaling, data, or failure boundary.
Never introduce layers, buses, repositories, factories, or services only to make the folder tree resemble a diagram.
Protect these boundaries
- Modules expose a small public API. Providers are private unless another module has a real use for them.
- A business capability owns its invariants and writes. Other modules collaborate through an exported application service, port, event, or deliberately designed read model.
- Dependencies point toward policy. Domain code does not import NestJS, an ORM, HTTP types, queue jobs, or vendor SDKs.
- Controllers and transport handlers adapt. They authenticate/authorize through framework mechanisms, validate transport input, invoke an application operation, and map the result.
- Application operations coordinate. They define use-case flow and transaction intent without absorbing persistence or transport details.
- Infrastructure implements edges. ORMs, brokers, caches, file systems, and external APIs stay behind explicit boundaries when their volatility or test cost warrants it.
- Cycles are design feedback. Treat
forwardRef()as a last-resort compatibility tool, not the default fix. - The composition root owns wiring. Nest modules and providers assemble implementations; business objects do not locate dependencies at runtime.
For the consolidated rule set, load references/architecture-rules.md. For detailed NestJS boundaries, load references/module-boundaries.md and references/dependency-injection.md.
Apply principles pragmatically
Use references/engineering-principles.md as decision tests, not slogans.
When principles conflict, prefer this order:
- correctness, security, and data integrity;
- explicit external contracts and backward compatibility;
- operability and failure containment;
- established repository conventions;
- design purity and local elegance.
KISS does not mean hiding failure cases. DRY does not mean abstracting coincidental similarity. SOLID does not require one class per verb. YAGNI does not excuse a boundary already demanded by a known requirement.
Workflow
For a new design
- Define goals, constraints, non-goals, critical quality attributes, and expected change axes.
- Choose the lowest architecture level that satisfies them.
- Name capabilities and ownership before naming technical layers.
- Draw dependencies and data ownership. Reject ambiguous write ownership.
- Specify synchronous calls, events, transactions, and failure behavior.
- Produce one recommended design and explain what it intentionally does not optimize.
- Define architecture tests, integration tests, and operational checks that protect the decision.
For an implementation or refactor
- Trace the relevant request, message, or job end to end.
- Identify the smallest boundary violation that causes the problem.
- Move behavior toward its owner before adding an abstraction.
- Introduce a port only if there are multiple implementations, a volatile edge, or a valuable test seam.
- Keep the change incremental and preserve the public contract unless the user requested a migration.
- Update affected tests, module wiring, documentation, and callers.
For a review
Use references/architecture-review.md. Report evidence-backed findings with severity, impact, a minimal remedy, and a validation step. Do not propose a rewrite when a boundary repair is sufficient.
Reference routing
| Task | Load |
|---|---|
| Apply or review the consolidated architecture rules | architecture-rules.md |
| Choose modular monolith, layers, hexagonal, CQRS, or services | architecture-ladder.md |
| Design feature modules, exports, ownership, or remove cycles | module-boundaries.md |
| Define provider tokens, ports, scopes, factories, or dynamic modules | dependency-injection.md |
| Define database ownership, repositories, migrations, transactions, or ORM boundaries | database-orm.md |
| Decide service extraction, distributed data ownership, contracts, or messaging semantics | microservices.md |
| Apply KISS, YAGNI, DRY, cohesion, coupling, and dependency rules | engineering-principles.md |
| Audit a repository or plan an incremental migration | architecture-review.md |
Expected response
For architecture work, make the result easy to evaluate:
- Baseline: current architecture and evidence.
- Decision: one recommended direction.
- Why: constraints and trade-offs that drive it.
- Boundaries: module ownership, dependency direction, data ownership, and integration style.
- Migration: smallest safe sequence, when change is needed.
- Validation: tests, graph checks, runtime checks, and operational signals.
If no architecture change is justified, say so directly.
Canonical source: skills/nestjs-architecture-principles/SKILL.md. This page is generated during the documentation build.