| name | architecting-nestjs-modules |
| description | Designs NestJS feature modules with clean dependency boundaries, provider scopes, and dynamic modules. Use when adding a new feature module, splitting a monolithic AppModule, or fixing tangled imports between modules. |
| license | MIT |
Architecting NestJS Modules
When to use
- Adding a new feature module
- Splitting a monolithic
AppModule
- Fixing tangled imports between modules
Core rules
- Feature modules own their domain, application, infrastructure layers
- Providers default to
Scope.DEFAULT (singleton), use Scope.REQUEST only when needed
- Export only what other modules need (don't export everything)
- Dynamic modules for configurable features (database, cache)
- No cross-feature imports; use shared contracts
Reference shape (TypeScript)
Feature Module
@Module({
controllers: [UserController],
providers: [
CreateUserUseCase,
{ provide: 'UserRepository', useClass: TypeOrmUserRepository },
],
exports: [CreateUserUseCase],
})
export class UserModule {}
Dynamic Module Example
@Module({})
export class DatabaseModule {
static register(config: DatabaseConfig): DynamicModule {
return {
module: DatabaseModule,
providers: [
{ provide: 'DATABASE_CONFIG', useValue: config },
{ provide: DataSource, useFactory: () => new DataSource(config).initialize() },
],
exports: [DataSource],
};
}
}
Examples — Do
@Module({ controllers: [OrderController], providers: [OrderService], exports: [OrderService] })
export class OrderModule {}
Examples — Don't
@Module({ controllers: [UserController, OrderController, ProductController], providers: [...] })
export class AppModule {}
Checklist
See reference/module-patterns.md for full patterns.