260223:1415 20260223 nextJS & nestJS Best pratices
All checks were successful
Build and Deploy / deploy (push) Successful in 4m44s
All checks were successful
Build and Deploy / deploy (push) Successful in 4m44s
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
---
|
||||
title: Use Proper Module Sharing Patterns
|
||||
impact: CRITICAL
|
||||
impactDescription: Prevents duplicate instances, memory leaks, and state inconsistency
|
||||
tags: architecture, modules, sharing, exports
|
||||
---
|
||||
|
||||
## Use Proper Module Sharing Patterns
|
||||
|
||||
NestJS modules are singletons by default. When a service is properly exported from a module and that module is imported elsewhere, the same instance is shared. However, providing a service in multiple modules creates separate instances, leading to memory waste, state inconsistency, and confusing behavior. Always encapsulate services in dedicated modules, export them explicitly, and import the module where needed.
|
||||
|
||||
**Incorrect (service provided in multiple modules):**
|
||||
|
||||
```typescript
|
||||
// StorageService provided directly in multiple modules - WRONG
|
||||
// storage.service.ts
|
||||
@Injectable()
|
||||
export class StorageService {
|
||||
private cache = new Map(); // Each instance has separate state!
|
||||
|
||||
store(key: string, value: any) {
|
||||
this.cache.set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
// app.module.ts
|
||||
@Module({
|
||||
providers: [StorageService], // Instance #1
|
||||
controllers: [AppController],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
// videos.module.ts
|
||||
@Module({
|
||||
providers: [StorageService], // Instance #2 - different from AppModule!
|
||||
controllers: [VideosController],
|
||||
})
|
||||
export class VideosModule {}
|
||||
|
||||
// Problems:
|
||||
// 1. Two separate StorageService instances exist
|
||||
// 2. cache.set() in VideosModule doesn't affect AppModule's cache
|
||||
// 3. Memory wasted on duplicate instances
|
||||
// 4. Debugging nightmares when state doesn't sync
|
||||
```
|
||||
|
||||
**Correct (dedicated module with exports):**
|
||||
|
||||
```typescript
|
||||
// storage/storage.module.ts
|
||||
@Module({
|
||||
providers: [StorageService],
|
||||
exports: [StorageService], // Make available to importers
|
||||
})
|
||||
export class StorageModule {}
|
||||
|
||||
// videos/videos.module.ts
|
||||
@Module({
|
||||
imports: [StorageModule], // Import the module, not the service
|
||||
controllers: [VideosController],
|
||||
providers: [VideosService],
|
||||
})
|
||||
export class VideosModule {}
|
||||
|
||||
// channels/channels.module.ts
|
||||
@Module({
|
||||
imports: [StorageModule], // Same instance shared
|
||||
controllers: [ChannelsController],
|
||||
providers: [ChannelsService],
|
||||
})
|
||||
export class ChannelsModule {}
|
||||
|
||||
// app.module.ts
|
||||
@Module({
|
||||
imports: [
|
||||
StorageModule, // Only if AppModule itself needs StorageService
|
||||
VideosModule,
|
||||
ChannelsModule,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
// Now all modules share the SAME StorageService instance
|
||||
```
|
||||
|
||||
**When to use @Global() (sparingly):**
|
||||
|
||||
```typescript
|
||||
// ONLY for truly cross-cutting concerns
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [ConfigService, LoggerService],
|
||||
exports: [ConfigService, LoggerService],
|
||||
})
|
||||
export class CoreModule {}
|
||||
|
||||
// Import once in AppModule
|
||||
@Module({
|
||||
imports: [CoreModule], // Registered globally, available everywhere
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
// Other modules don't need to import CoreModule
|
||||
@Module({
|
||||
controllers: [UsersController],
|
||||
providers: [UsersService], // Can inject ConfigService without importing
|
||||
})
|
||||
export class UsersModule {}
|
||||
|
||||
// WARNING: Don't make everything global!
|
||||
// - Hides dependencies (can't see what a module needs from imports)
|
||||
// - Makes testing harder
|
||||
// - Reserve for: config, logging, database connections
|
||||
```
|
||||
|
||||
**Module re-exporting pattern:**
|
||||
|
||||
```typescript
|
||||
// common.module.ts - shared utilities
|
||||
@Module({
|
||||
providers: [DateService, ValidationService],
|
||||
exports: [DateService, ValidationService],
|
||||
})
|
||||
export class CommonModule {}
|
||||
|
||||
// core.module.ts - re-exports common for convenience
|
||||
@Module({
|
||||
imports: [CommonModule, DatabaseModule],
|
||||
exports: [CommonModule, DatabaseModule], // Re-export for consumers
|
||||
})
|
||||
export class CoreModule {}
|
||||
|
||||
// feature.module.ts - imports CoreModule, gets both
|
||||
@Module({
|
||||
imports: [CoreModule], // Gets CommonModule + DatabaseModule
|
||||
controllers: [FeatureController],
|
||||
})
|
||||
export class FeatureModule {}
|
||||
```
|
||||
|
||||
Reference: [NestJS Modules](https://docs.nestjs.com/modules#shared-modules)
|
||||
Reference in New Issue
Block a user