| name | nestjs-architecture |
| description | Design NestJS module boundaries, provider scope, and request pipeline choices. Use when structuring modules, placing guards/pipes/interceptors, or reviewing provider lifetime in NestJS. |
| metadata | {"triggers":{"files":["**/*.module.ts","main.ts"],"keywords":["NestFactory","Module","Controller","Injectable"]}} |
NestJS Architecture Expert
Priority: P0 (CRITICAL)
Design feature modules with singleton-first providers and explicit request-pipeline choices.
Decision Map
- Module role: Feature Modules (Auth) vs Core (Config/DB) vs Shared (Utils). Feature owns its controllers, application services, and adapters. Shared exports stateless helpers only.
- Provider scope: default singleton. Use request scope only for tenant context, request-local caching, or request tracking. Treat request scope as a measured exception.
- Pipeline choice: middleware for raw HTTP concerns, guard for access decisions, pipe for validation/transforms, interceptor for cross-cutting request/response behavior, filter for error translation.
Recipe
- Create bounded feature: module + controller + application service + persistence adapter.
- Keep controller thin: Thin controllers, fat services. No business in Controller. Move logic to Service.
- Register dependencies once: imports for modules, providers for services, exports only for true consumers.
@InjectRepository() dependencies need TypeOrmModule.forFeature([...]).
- Validate at edges: DTO validation pipes for incoming data; never trust raw payloads in services.
- Check circular imports: check circular dependencies with
madge; refactor contracts first and use forwardRef() only as last resort.
Verify
Anti-Patterns
- No request scope by default: Singleton first; justify heavier scope.
- No business logic in controllers: Delegate orchestration to services/use cases.
- No entity leakage: Don't return ORM entities; return DTOs or response models.
- No manual instantiation: Use DI; never
new Service() inside Nest code.
References