Module Scaffold (Tạo khung Module NestJS)
Tạo đầy đủ file structure cho domain module $ARGUMENTS theo chuẩn PMTL_VN.
Bước 1: Đọc design contracts
Trước khi tạo bất kỳ file nào, đọc:
design/<module-number>-$ARGUMENTS/module-map.md — hiểu objectives và boundaries
design/<module-number>-$ARGUMENTS/contracts.md — hiểu routes, input schemas, rules
design/<module-number>-$ARGUMENTS/schema.dbml — hiểu DB schema
design/<module-number>-$ARGUMENTS/use-cases/ — đọc tất cả use-case files
design/baseline/nest-baseline.md — chuẩn NestJS của project
design/01-identity/PERMISSION_MATRIX.md — per-module permission scope cho module này
Nếu không tìm thấy module trong design/, hỏi lại tên chính xác trước khi tiếp tục.
Bước 2: Xác nhận trước khi tạo
Tóm tắt cho user:
- Module này sở hữu collections nào
- Routes sẽ được tạo
- Permission scope cho từng role
- Dependencies vào module khác
Hỏi: "Xác nhận tạo scaffold cho module này không?"
Bước 3: Tạo file structure
Target path: apps/api/src/modules/$ARGUMENTS/
File structure cần tạo:
apps/api/src/modules/$ARGUMENTS/
├── $ARGUMENTS.module.ts # NestJS module definition
├── $ARGUMENTS.controller.ts # HTTP layer — thin, chỉ validate + delegate
├── $ARGUMENTS.service.ts # Business logic
├── $ARGUMENTS.repository.ts # Prisma queries — không có business logic
├── $ARGUMENTS.schemas.ts # Zod schemas cho tất cả inputs
├── $ARGUMENTS.mapper.ts # Entity → DTO mapping (không expose raw DB)
├── $ARGUMENTS.policy.ts # Authorization rules (role + business rule)
└── dto/
├── create-$ARGUMENTS.dto.ts # Response DTOs
└── $ARGUMENTS-response.dto.ts
Rules cho mỗi file:
$ARGUMENTS.module.ts
- Import PrismaModule, AuditModule, platform modules cần thiết
- Exports service nếu module khác cần đọc
$ARGUMENTS.controller.ts
@UseGuards(JwtAuthGuard, RolesGuard) trên routes cần auth
@RateLimit(...) trên auth/write/upload endpoints
- Chỉ validate input (dùng schema từ
.schemas.ts) + gọi service
- Không có business logic trong controller
- Mỗi route return DTO đã mapped, không phải raw Prisma entity
$ARGUMENTS.service.ts
- Inject repository, auditService, feature flags
- Mọi write operation phải:
- Validate business invariants
- Execute canonical write (qua repository)
auditService.append(...) với actor + action + entityId
- Return mapped DTO
- Side effects async (outbox phase 2+ / inline sync phase 1)
$ARGUMENTS.repository.ts
- Chỉ Prisma queries
- Không có business logic
- Trả về Prisma entity — mapping xảy ra ở service/mapper
- Tên method rõ ràng:
findByPublicId, createOne, updateStatus, etc.
$ARGUMENTS.schemas.ts
- Zod schema cho mọi input: request body, query params, path params
- Export const theo tên:
CreateXxxSchema, UpdateXxxSchema, XxxQuerySchema
- Không dùng
z.any() hoặc z.unknown() trừ khi có lý do rõ
$ARGUMENTS.mapper.ts
toResponseDto(entity: PrismaEntity): XxxResponseDto
- Không expose: password_hash, raw tokens, internal IDs, sensitive fields
- publicId thay cho id trong responses
$ARGUMENTS.policy.ts
canRead(actor: AuthUser, entity: Xxx): boolean
canWrite(actor: AuthUser, entity?: Xxx): boolean
canDelete(actor: AuthUser, entity: Xxx): boolean
- Tách rõ: role check vs business rule vs deletion policy
Bước 4: Thêm placeholder comments
Mỗi file phải có comment ở đầu:
/**
* @module $ARGUMENTS
* @owner PMTL_VN $ARGUMENTS module
* @ref design/<number>-$ARGUMENTS/contracts.md
*
* Canonical collections: [list từ module-map.md]
* Does NOT own: [list từ module-map.md]
*/
Bước 5: Tạo migration placeholder
Tạo file prisma/migrations/PLACEHOLDER_$ARGUMENTS/README.md với:
- Schema tables cần tạo (từ schema.dbml)
- Reminder: phải có migration thật trước khi implement
Bước 6: Báo cáo kết quả
Liệt kê tất cả file đã tạo và:
- Những gì cần implement tiếp (business logic chưa có)
- Use-case files cần đọc trước khi code từng feature
- Audit events bắt buộc cho module này (từ tracking/audit-policy.md)
Quan trọng
- Không tạo file nếu chưa đọc design contracts
- Không implement business logic thật — chỉ scaffold structure và comments
- Không bỏ qua
.schemas.ts — mọi input phải có Zod schema
- Không để controller có business logic
- Không expose raw Prisma entity trong response — phải qua mapper
1---2name: module-scaffold-23description: Scaffold a new NestJS domain module for PMTL_VN following project conventions. Creates the correct file structure with module, controller, service, repository, schemas, mapper, and policy files. Use when starting to implement a new domain module from the design.4---5
6# Module Scaffold (Tạo khung Module NestJS)
7
8Tạo đầy đủ file structure cho domain module **$ARGUMENTS** theo chuẩn PMTL_VN.
9
10## Bước 1: Đọc design contracts
11
12Trước khi tạo bất kỳ file nào, đọc:
131. `design/<module-number>-$ARGUMENTS/module-map.md` — hiểu objectives và boundaries
142. `design/<module-number>-$ARGUMENTS/contracts.md` — hiểu routes, input schemas, rules
153. `design/<module-number>-$ARGUMENTS/schema.dbml` — hiểu DB schema
164. `design/<module-number>-$ARGUMENTS/use-cases/` — đọc tất cả use-case files
175. `design/baseline/nest-baseline.md` — chuẩn NestJS của project
186. `design/01-identity/PERMISSION_MATRIX.md` — per-module permission scope cho module này
19
20Nếu không tìm thấy module trong design/, hỏi lại tên chính xác trước khi tiếp tục.
21
22## Bước 2: Xác nhận trước khi tạo
23
24Tóm tắt cho user:
25- Module này sở hữu collections nào
26- Routes sẽ được tạo
27- Permission scope cho từng role
28- Dependencies vào module khác
29
30Hỏi: "Xác nhận tạo scaffold cho module này không?"
31
32## Bước 3: Tạo file structure
33
34Target path: `apps/api/src/modules/$ARGUMENTS/`
35
36### File structure cần tạo:
37
38```
39apps/api/src/modules/$ARGUMENTS/
40├── $ARGUMENTS.module.ts # NestJS module definition
41├── $ARGUMENTS.controller.ts # HTTP layer — thin, chỉ validate + delegate
42├── $ARGUMENTS.service.ts # Business logic
43├── $ARGUMENTS.repository.ts # Prisma queries — không có business logic
44├── $ARGUMENTS.schemas.ts # Zod schemas cho tất cả inputs
45├── $ARGUMENTS.mapper.ts # Entity → DTO mapping (không expose raw DB)
46├── $ARGUMENTS.policy.ts # Authorization rules (role + business rule)
47└── dto/
48 ├── create-$ARGUMENTS.dto.ts # Response DTOs
49 └── $ARGUMENTS-response.dto.ts
50```
51
52### Rules cho mỗi file:
53
54**`$ARGUMENTS.module.ts`**
55- Import PrismaModule, AuditModule, platform modules cần thiết
56- Exports service nếu module khác cần đọc
57
58**`$ARGUMENTS.controller.ts`**
59- `@UseGuards(JwtAuthGuard, RolesGuard)` trên routes cần auth
60- `@RateLimit(...)` trên auth/write/upload endpoints
61- Chỉ validate input (dùng schema từ `.schemas.ts`) + gọi service
62- Không có business logic trong controller
63- Mỗi route return DTO đã mapped, không phải raw Prisma entity
64
65**`$ARGUMENTS.service.ts`**
66- Inject repository, auditService, feature flags
67- Mọi write operation phải:
68 1. Validate business invariants
69 2. Execute canonical write (qua repository)
70 3. `auditService.append(...)` với actor + action + entityId
71 4. Return mapped DTO
72- Side effects async (outbox phase 2+ / inline sync phase 1)
73
74**`$ARGUMENTS.repository.ts`**
75- Chỉ Prisma queries
76- Không có business logic
77- Trả về Prisma entity — mapping xảy ra ở service/mapper
78- Tên method rõ ràng: `findByPublicId`, `createOne`, `updateStatus`, etc.
79
80**`$ARGUMENTS.schemas.ts`**
81- Zod schema cho **mọi** input: request body, query params, path params
82- Export const theo tên: `CreateXxxSchema`, `UpdateXxxSchema`, `XxxQuerySchema`
83- Không dùng `z.any()` hoặc `z.unknown()` trừ khi có lý do rõ
84
85**`$ARGUMENTS.mapper.ts`**
86- `toResponseDto(entity: PrismaEntity): XxxResponseDto`
87- Không expose: password_hash, raw tokens, internal IDs, sensitive fields
88- publicId thay cho id trong responses
89
90**`$ARGUMENTS.policy.ts`**
91- `canRead(actor: AuthUser, entity: Xxx): boolean`
92- `canWrite(actor: AuthUser, entity?: Xxx): boolean`
93- `canDelete(actor: AuthUser, entity: Xxx): boolean`
94- Tách rõ: role check vs business rule vs deletion policy
95
96## Bước 4: Thêm placeholder comments
97
98Mỗi file phải có comment ở đầu:
99```typescript
100/**
101 * @module $ARGUMENTS
102 * @owner PMTL_VN $ARGUMENTS module
103 * @ref design/<number>-$ARGUMENTS/contracts.md
104 *
105 * Canonical collections: [list từ module-map.md]
106 * Does NOT own: [list từ module-map.md]
107 */
108```
109
110## Bước 5: Tạo migration placeholder
111
112Tạo file `prisma/migrations/PLACEHOLDER_$ARGUMENTS/README.md` với:
113- Schema tables cần tạo (từ schema.dbml)
114- Reminder: phải có migration thật trước khi implement
115
116## Bước 6: Báo cáo kết quả
117
118Liệt kê tất cả file đã tạo và:
119- Những gì cần implement tiếp (business logic chưa có)
120- Use-case files cần đọc trước khi code từng feature
121- Audit events bắt buộc cho module này (từ tracking/audit-policy.md)
122
123---
124
125## Quan trọng
126
127- **Không** tạo file nếu chưa đọc design contracts
128- **Không** implement business logic thật — chỉ scaffold structure và comments
129- **Không** bỏ qua `.schemas.ts` — mọi input phải có Zod schema
130- **Không** để controller có business logic
131- **Không** expose raw Prisma entity trong response — phải qua mapper