Skip to content

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.

Pre-execution conflict guard

Run this guard after identifying every applicable skill and before editing files, installing packages, generating code, running migrations, or executing any other state-changing command. Read-only inspection is allowed while resolving the guard.

Prerequisites

  • Read the target repository's instructions, manifests, module/bootstrap files, tests, and the coordination contract of every other active skill.
  • Identify the requested outcome, public contracts that must remain stable, and the files and commands likely to be affected.
  • Do not assume that activating this skill gives it ownership of every NestJS decision.

Primary ownership

This skill leads decisions about architecture level, capability and module boundaries, dependency direction, public module APIs, data/write ownership, transaction boundaries, ports, and service extraction.

It shares provider/module placement with nestjs-oop-design-patterns and shares transport, deployment, and scaling boundaries with nestjs-features-performance. It yields local object responsibilities and pattern selection to the OOP skill, and yields NestJS lifecycle mechanisms, error/transport mapping, security controls, testing strategy, runtime performance, and delivery operations to the features skill.

For a whole-repository review, nestjs-code-audit owns read-only evidence collection, deduplication, and report assembly while this skill remains the primary owner of architecture findings.

For a roadmap-scoped review, nestjs-feature-audit owns target-branch preparation, roadmap traceability, status classification, and report assembly while this skill remains the primary owner of architecture judgments.

For implementation, nestjs-professional-software-engineering coordinates project inspection, syntax selection, coding, and verification while this skill remains the primary owner of NestJS architecture boundaries.

nestjs-git-commit-pr-message may publish the verified change but does not alter architecture decisions to simplify Git history or release text.

Conflict test

A conflict exists when active skills would:

  • change the same file or contract toward incompatible outcomes;
  • require commands whose order, environment, or side effects cannot both be satisfied;
  • claim primary ownership of the same decision without a clear handoff; or
  • proceed while another skill's prerequisite or repository constraint is unmet.

Resolve conflicts in this order: explicit user intent, repository contracts and verified runtime constraints, then the narrowest primary owner above. Assign one lead skill per disputed decision; other skills may advise only within that boundary. For example, this skill decides whether application code may depend on HTTP types, while the features skill decides how a transport filter maps the resulting failure.

If the conflict remains material, stop before mutation and ask for clarification. Report the conflicting instructions, affected files or commands, why both cannot be satisfied, and the smallest safe choices. Never silently blend incompatible architectures or let whichever skill runs last overwrite the earlier decision.

Start with evidence

Before recommending or changing architecture:

  1. Read package.json, nest-cli.json, TypeScript configuration, bootstrap files, and the relevant modules.
  2. Map the module import graph, provider exports, entry points, persistence ownership, transports, and existing tests.
  3. Identify the business capability and the concrete change pressure: team ownership, independent scaling, volatile integrations, transaction boundaries, security, or delivery speed.
  4. State the current architecture in one sentence. Do not label a project "Clean Architecture" or "DDD" from folder names alone.
  5. 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

  1. Modules expose a small public API. Providers are private unless another module has a real use for them.
  2. A business capability owns its invariants and writes. Other modules collaborate through an exported application service, port, event, or deliberately designed read model.
  3. Dependencies point toward policy. Domain code does not import NestJS, an ORM, HTTP types, queue jobs, or vendor SDKs.
  4. Controllers and transport handlers adapt. They authenticate/authorize through framework mechanisms, validate transport input, invoke an application operation, and map the result.
  5. Application operations coordinate. They define use-case flow and transaction intent without absorbing persistence or transport details.
  6. Infrastructure implements edges. ORMs, brokers, caches, file systems, and external APIs stay behind explicit boundaries when their volatility or test cost warrants it.
  7. Cycles are design feedback. Treat forwardRef() as a last-resort compatibility tool, not the default fix.
  8. 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:

  1. correctness, security, and data integrity;
  2. explicit external contracts and backward compatibility;
  3. operability and failure containment;
  4. established repository conventions;
  5. 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

  1. Define goals, constraints, non-goals, critical quality attributes, and expected change axes.
  2. Choose the lowest architecture level that satisfies them.
  3. Name capabilities and ownership before naming technical layers.
  4. Draw dependencies and data ownership. Reject ambiguous write ownership.
  5. Specify synchronous calls, events, transactions, and failure behavior.
  6. Produce one recommended design and explain what it intentionally does not optimize.
  7. Define architecture tests, integration tests, and operational checks that protect the decision.

For an implementation or refactor

  1. Trace the relevant request, message, or job end to end.
  2. Identify the smallest boundary violation that causes the problem.
  3. Move behavior toward its owner before adding an abstraction.
  4. Introduce a port only if there are multiple implementations, a volatile edge, or a valuable test seam.
  5. Keep the change incremental and preserve the public contract unless the user requested a migration.
  6. 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

TaskLoad
Apply or review the consolidated architecture rulesarchitecture-rules.md
Choose modular monolith, layers, hexagonal, CQRS, or servicesarchitecture-ladder.md
Design feature modules, exports, ownership, or remove cyclesmodule-boundaries.md
Define provider tokens, ports, scopes, factories, or dynamic modulesdependency-injection.md
Define database ownership, repositories, migrations, transactions, or ORM boundariesdatabase-orm.md
Decide service extraction, distributed data ownership, contracts, or messaging semanticsmicroservices.md
Apply KISS, YAGNI, DRY, cohesion, coupling, and dependency rulesengineering-principles.md
Audit a repository or plan an incremental migrationarchitecture-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.

Open-source guidance for deliberate NestJS engineering.