NestJS Architecture Standards
Core Principles
- Modularity: Every feature must be encapsulated in its own
@Module.- Do:
users.module.ts,auth.module.ts. - Don't: Everything in
app.module.ts.
- Do:
- Dependency Injection (DI): Invert control. Never manually instantiate classes (e.g.,
new Service()).- Use: Constructor injection
constructor(private readonly service: Service).
- Use: Constructor injection
- Scalability: Use Feature Modules for domain logic and Core/Shared Modules for reusable utilities.
Module Configuration
Dynamic Modules
- Modern Pattern: Use
ConfigurableModuleBuilderclass to auto-generateforRoot/registermethods properly. - Reference: See Dynamic Module Builder Implementation for the boilerplate code.
- Conventions:
forRoot: Global configurations (Db, Config).register: Per-instance configurations.forFeature: Extending a module with specific providers/entities.
- Conventions:
Circular Dependencies
- Avoid: Re-architect to move shared logic to a common module.
- Constraint: If unavoidable, use
forwardRef(() => ModuleName)on both sides of the import.
Advanced Providers
- Factory Providers: Use
useFactoryheavily for providers dependent on configuration or async operations. - Aliasing: Use
useExistingto provide backward compatibility or abstract different implementations.
Scopes & Lifecycle
- Default: Singleton. Best performance.
- Request Scope: Use
Scope.REQUESTsparingly.- Performance Warning: Request scope bubbles up. If a Service is request-scoped, every controller injecting it becomes request-scoped, triggering re-instantiation per request (~5-10% latency overhead).
- Multi-tenancy: If request-scope is needed (e.g. Tenant ID header), use Durable Providers (
durable: true) withContextIdFactoryto reuse DI sub-trees. - Shutdown:
SIGTERMdoesn't trigger cleanup by default.- Mandatory: Call
app.enableShutdownHooks()inmain.ts.
- Mandatory: Call
Structure & Organization
- Feature Modules: Domain logic (
ShopModule,AuthModule). Encapsulated. - Shared Module: Reusable providers (
DateService,MathService) exported to other modules. Stateless. - Core Module:
- Role: Global infrastructure setup ONE TIME (Interceptors, Filters, Loggers).
- Rule: Import
CoreModuleonly inAppModule. - Contents:
APP_INTERCEPTOR,APP_FILTER,APP_GUARDproviders.
Reliability & Observability
- Health Checks: Mandatory for K8s/Docker.
- Tool: Use
@nestjs/terminus. Expose/healthendpoint checking DB, Cache (Redis), and Memory.
- Tool: Use
- Structured Logging:
- Warning: Default NestJS logger is unstructured text.
- Standard: Use
nestjs-pinofor JSON-formatted logs with automaticreq-idcorrelation and request duration tracking. - Context: Inject
Loggerinto services to keep traces connected.