使い方
- インストールコマンドをコピーし、自分の環境へ導入する前にソースを確認します。
- 必要なツール、認証情報、対応環境を原文で確認します。
- 入力と成果物を指定し、出力を資料と照合します。
npx skills add https://github.com/mattiavalerio/mvskills --skill nestjs-architecture開始前の確認
- 導入にはモデル契約、API枠、ファイルへのアクセス許可は含まれません。
- スター数や導入数は活動の指標であり、独立した品質・安全性試験ではありません。
見出し、例、コード、表、リンク、参照画像を含む原文を表示しています。
Profile scope and official documentation
Read docs/agents/stack.md when present. Apply this custom stack only to apps selecting the default NestJS profile or explicitly adopting it. In existing apps with another stack preserve conventions; this skill does not authorize migration. For new unconfigured apps agree on the profile using setup-mvskills.
For API/CLI facts start with https://docs.nestjs.com/llms.txt and fetch relevant linked official pages. Use https://docs.nestjs.com/llms-full.txt as a fallback, searching relevant sections rather than loading it wholesale. Match docs to installed versions and command --help; the selected profile owns architecture choices.
NestJS — Clean Architecture with Vertical Slices
This skill tells you what goes where and who may depend on whom. How to generate files with the CLI lives in the nestjs-cli skill; the step-by-step workflow for adding a feature lives in the nestjs-feature skill.
The mental model
Load strict-typescript for authored code, scripts and review. For public HTTP DTOs, responses and frontend contracts load api-contracts when selected by the profile. Use the host's skill tool for these loads, or read the installed entrypoints when it is unavailable. Repository standards and ADRs remain authoritative for the selected app; pass their paths along with this profile to Standards review.
A backend is a set of bounded contexts (orders, billing, catalog…). Each context is one Nest module. Inside a context, every use case is a vertical slice: one folder that holds everything specific to that use case (HTTP entry point, input schema, application logic, tests). What is shared inside a context — the domain model and the persistence contracts — sits next to the slices, not inside them.
src/
├── main.ts
├── app.module.ts # imports context modules + shared infrastructure only
├── modules/
│ └── <context>/
│ ├── <context>.module.ts # composition root of the context (wiring only)
│ ├── <context>.http-errors.ts # domain error type → HTTP status table
│ ├── domain/ # entities, value objects, domain errors — pure TS
│ ├── ports/ # abstract classes the use cases depend on
│ ├── infrastructure/ # adapters implementing ports (Kysely, HTTP clients…)
│ ├── public/ # (optional) what other contexts may import
│ └── features/
│ └── <feature>/ # one folder per use case, kebab-case verb-noun
│ ├── <feature>.controller.ts
│ ├── <feature>.dto.ts
│ ├── <feature>.use-case.ts
│ └── <feature>.use-case.spec.ts
└── shared/
├── kernel/ # cross-context pure code: base error types, Result helpers
├── http/ # Result → HttpException mapping, global pipes/filters
└── infrastructure/ # DatabaseModule (Kysely), config, loggerFeature names are use cases, not CRUD verbs on tables: place-order, cancel-order, get-order-summary — not orders-crud. If you cannot name the slice as an action a user or system performs, the slice is probably wrong.
Dependency rules (the part that matters most)
Dependencies point inwards. These rules are enforced by dependency-cruiser (config in assets/dependency-cruiser.cjs), so breaking them fails validation.
- `domain/` is pure. No imports from
@nestjs/*,kysely,nestjs-zod, other
layers of the context, or other contexts. It may import neverthrow and shared/kernel. Why: domain logic must be testable and portable without booting Nest.
- `ports/` depend only on `domain/` and `shared/kernel`. Ports are
abstract classes,
which double as Nest injection tokens — no @Inject() strings or symbols needed.
- A slice never imports another slice. If two slices need the same logic, it belongs
in domain/ (business rule) or in a port (data access). Duplicating a small mapping between two slices is acceptable; coupling them is not. Why: slices must be deletable and developable in parallel without touching each other.
- Slices never import `infrastructure/`. They depend on ports; the module binds the
port to an adapter.
- Contexts talk through `public/` only (exported types, a facade port, or domain
events). Never import another context's domain/, features/ or infrastructure/.
- `<context>.module.ts` is the only file that knows everything. It contains wiring
(controllers, providers, { provide: Port, useClass: Adapter }) and nothing else.
Layer responsibilities — short version
| Layer | Contains | Never contains |
|---|---|---|
| Controller | route decorators, DTO in, call use case, map Result → HTTP | business rules, DB access, try/catch for flow control |
| DTO | Zod schema + createZodDto | logic, entity types |
| Use case | orchestration, returns ResultAsync<Output, Error> | HTTP concepts, Kysely, throw for expected failures |
| Domain | entities, invariants, factory functions returning Result | framework imports, I/O |
| Port | abstract class with method signatures returning ResultAsync | implementation |
| Adapter | Kysely queries, row ↔ entity mapping, infra errors → domain errors | business decisions |
Conventions you must follow
- TypeScript strict, zero `any`. Use
unknown+ Zod parsing at boundaries. - Expected failures are values, not exceptions. Use cases and ports return
ResultAsync<T, E> from neverthrow. Only truly unexpected failures (bugs, DB down) may throw. Details: references/errors-and-results.md.
- Validation happens once, at the edge, with Zod via
nestjs-zod. Inside the slice,
data is already typed and trusted.
- Kysely only for persistence (never Prisma, Drizzle or TypeORM, even though the Nest
docs suggest them). Details: references/persistence-kysely.md.
- No premature abstraction. Don't add a port for something with one trivial
implementation that will never change (e.g. a clock in a CRUD app). Add it when you need to test against it or swap it.
- One use case = one class with one public `execute()` method.
- In the selected mvskills profile use extensionless @/ imports rooted at app src,
e.g. @/shared/kernel/errors. Follow strict-typescript's alias reference for build, watch, test and runtime resolution. Preserve existing import styles until migrated.
When to read the references
references/slice-anatomy.md— full annotated example of one slice, plus the module file.
Read it the first time you create a slice in a session.
references/errors-and-results.md— domain error shapes, neverthrow patterns,
Result → HTTP mapping. Read it when writing a use case, port or controller.
references/persistence-kysely.md— repository adapters, mapping, transactions.
Read it when touching infrastructure/.
references/testing-vitest.md— what to test at which level, fakes vs mocks, e2e.
Read it before writing tests.
references/decision-guide.md— "where does this go?" for ambiguous cases (shared
logic, cross-context calls, queries vs commands, background jobs).
Bootstrapping a new project
If src/shared/ does not exist yet:
- Copy
assets/shared/intosrc/shared/. - Copy
assets/dependency-cruiser.cjsto the project root as.dependency-cruiser.cjs. - Use
assets/app.module.example.tsas the reference forapp.module.ts(global
ZodValidationPipe + ZodSerializerInterceptor, config, database).
- Install:
pnpm add neverthrow zod nestjs-zod kysely pg @nestjs/configand
pnpm add -D dependency-cruiser kysely-codegen @types/pg. Verify dependency-cruiser supports the installed TypeScript version and actually analyzes sources. The validation helper fails on zero analyzed modules or a missing TypeScript transpiler; resolve compatibility using current tool documentation. Prove the rules bite: check:arch passes on the clean tree; a temporary import from a slice into infrastructure/ fails with slices-do-not-import-infrastructure; after reverting it passes again. If the planted import does not fail, fix the config or alias resolution before continuing. Leave no planted file behind.
- Generate DB types with
pnpm kysely-codegen --out-file src/shared/infrastructure/database/db.generated.ts.




