--- 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)