0
# Module System
1
2
InversifyJS provides a module system for organizing and packaging related service bindings into reusable units. Container modules allow you to group related configurations, support dynamic loading/unloading, and enable modular application architecture.
3
4
## ContainerModule Class
5
6
```typescript { .api }
7
class ContainerModule {
8
constructor(
9
registry: (
10
bind: BindFunction,
11
unbind: UnbindFunction,
12
isBound: IsBoundFunction,
13
rebind: RebindFunction
14
) => void
15
);
16
}
17
18
type BindFunction = <T>(serviceIdentifier: ServiceIdentifier<T>) => BindInFluentSyntax<T>;
19
type UnbindFunction = (serviceIdentifier: ServiceIdentifier) => void;
20
type IsBoundFunction = (serviceIdentifier: ServiceIdentifier) => boolean;
21
type RebindFunction = <T>(serviceIdentifier: ServiceIdentifier<T>) => BindInFluentSyntax<T>;
22
```
23
24
## Module Loading Options
25
26
```typescript { .api }
27
interface ContainerModuleLoadOptions {
28
skipDeactivation?: boolean;
29
}
30
```
31
32
## Basic Module Creation
33
34
### Simple Module
35
36
```typescript
37
import { ContainerModule } from "inversify";
38
39
// Create a module for authentication services
40
const authModule = new ContainerModule((bind) => {
41
bind<IAuthService>("AuthService").to(JwtAuthService).inSingletonScope();
42
bind<ITokenService>("TokenService").to(JwtTokenService).inSingletonScope();
43
bind<IPasswordService>("PasswordService").to(BcryptPasswordService).inSingletonScope();
44
bind<IUserRepository>("UserRepository").to(DatabaseUserRepository).inSingletonScope();
45
});
46
47
// Load the module
48
container.load(authModule);
49
```
50
51
### Module with All Registry Functions
52
53
```typescript
54
const databaseModule = new ContainerModule((bind, unbind, isBound, rebind) => {
55
// Conditional binding
56
if (!isBound("DatabaseConnection")) {
57
bind<IDatabaseConnection>("DatabaseConnection")
58
.to(PostgreSQLConnection)
59
.inSingletonScope();
60
}
61
62
// Repository bindings
63
bind<IUserRepository>("UserRepository").to(DatabaseUserRepository);
64
bind<IProductRepository>("ProductRepository").to(DatabaseProductRepository);
65
bind<IOrderRepository>("OrderRepository").to(DatabaseOrderRepository);
66
67
// Rebind if testing environment
68
if (process.env.NODE_ENV === "test") {
69
rebind<IDatabaseConnection>("DatabaseConnection").to(InMemoryConnection);
70
}
71
72
// Clean up any existing cache bindings
73
if (isBound("Cache")) {
74
unbind("Cache");
75
}
76
77
bind<ICache>("Cache").to(RedisCache).inSingletonScope();
78
});
79
```
80
81
## Module Categories and Examples
82
83
### Infrastructure Module
84
85
```typescript
86
const infrastructureModule = new ContainerModule((bind) => {
87
// Logging
88
bind<ILogger>("Logger").to(WinstonLogger).inSingletonScope();
89
90
// Configuration
91
bind<IConfigService>("ConfigService").to(EnvironmentConfigService).inSingletonScope();
92
93
// Caching
94
bind<ICache>("Cache").to(RedisCache).inSingletonScope();
95
96
// Message Queue
97
bind<IMessageQueue>("MessageQueue").to(RabbitMQService).inSingletonScope();
98
99
// Health Monitoring
100
bind<IHealthCheckService>("HealthCheck").to(HealthCheckService).inSingletonScope();
101
});
102
```
103
104
### Business Logic Module
105
106
```typescript
107
const businessModule = new ContainerModule((bind) => {
108
// Domain Services
109
bind<IUserService>("UserService").to(UserService);
110
bind<IProductService>("ProductService").to(ProductService);
111
bind<IOrderService>("OrderService").to(OrderService);
112
bind<IPaymentService>("PaymentService").to(PaymentService);
113
114
// Business Rules
115
bind<IPricingEngine>("PricingEngine").to(DynamicPricingEngine).inSingletonScope();
116
bind<IInventoryManager>("InventoryManager").to(InventoryManager).inSingletonScope();
117
bind<INotificationService>("NotificationService").to(EmailNotificationService);
118
});
119
```
120
121
### Data Access Module
122
123
```typescript
124
const dataModule = new ContainerModule((bind) => {
125
// Database Connection
126
bind<IDatabaseConnection>("DatabaseConnection")
127
.toDynamicValue(() => {
128
const config = container.get<IConfigService>("ConfigService");
129
return new PostgreSQLConnection(config.getDatabaseUrl());
130
})
131
.inSingletonScope();
132
133
// Repositories
134
bind<IUserRepository>("UserRepository").to(UserRepository);
135
bind<IProductRepository>("ProductRepository").to(ProductRepository);
136
bind<IOrderRepository>("OrderRepository").to(OrderRepository);
137
138
// Query Builders
139
bind<IQueryBuilder>("QueryBuilder").to(SqlQueryBuilder);
140
141
// Migrations
142
bind<IMigrationService>("MigrationService").to(MigrationService);
143
});
144
```
145
146
### External Services Module
147
148
```typescript
149
const externalServicesModule = new ContainerModule((bind) => {
150
// Payment Processors
151
bind<IPaymentProcessor>("PaymentProcessor")
152
.to(StripePaymentProcessor)
153
.whenTargetTagged("provider", "stripe");
154
155
bind<IPaymentProcessor>("PaymentProcessor")
156
.to(PayPalPaymentProcessor)
157
.whenTargetTagged("provider", "paypal");
158
159
// Email Service
160
bind<IEmailService>("EmailService").to(SendGridEmailService).inSingletonScope();
161
162
// SMS Service
163
bind<ISmsService>("SmsService").to(TwilioSmsService).inSingletonScope();
164
165
// File Storage
166
bind<IFileStorage>("FileStorage").to(S3FileStorage).inSingletonScope();
167
168
// Analytics
169
bind<IAnalyticsService>("AnalyticsService").to(GoogleAnalyticsService).inSingletonScope();
170
});
171
```
172
173
## Module Loading and Management
174
175
### Loading Multiple Modules
176
177
```typescript
178
// Load multiple modules at once
179
container.load(
180
infrastructureModule,
181
dataModule,
182
businessModule,
183
externalServicesModule
184
);
185
186
// Load modules sequentially
187
container.load(infrastructureModule);
188
container.load(dataModule);
189
container.load(businessModule);
190
```
191
192
### Async Module Loading
193
194
```typescript
195
// Load modules asynchronously
196
await container.loadAsync(
197
infrastructureModule,
198
dataModule,
199
businessModule
200
);
201
202
// Individual async loading
203
await container.loadAsync(infrastructureModule);
204
await container.loadAsync(dataModule);
205
```
206
207
### Module Unloading
208
209
```typescript
210
// Unload specific modules
211
container.unload(externalServicesModule);
212
213
// Unload multiple modules
214
container.unload(businessModule, dataModule);
215
216
// Async unloading
217
await container.unloadAsync(externalServicesModule);
218
```
219
220
### Conditional Module Loading
221
222
```typescript
223
// Environment-based module loading
224
if (process.env.NODE_ENV === "production") {
225
container.load(productionModule);
226
} else if (process.env.NODE_ENV === "test") {
227
container.load(testModule);
228
} else {
229
container.load(developmentModule);
230
}
231
232
// Feature-based module loading
233
if (process.env.FEATURE_ANALYTICS === "enabled") {
234
container.load(analyticsModule);
235
}
236
237
if (process.env.FEATURE_PREMIUM === "enabled") {
238
container.load(premiumFeaturesModule);
239
}
240
```
241
242
## Advanced Module Patterns
243
244
### Environment-Specific Modules
245
246
```typescript
247
// Base module with common bindings
248
const baseModule = new ContainerModule((bind) => {
249
bind<ILogger>("Logger").to(ConsoleLogger);
250
bind<IConfigService>("ConfigService").to(ConfigService).inSingletonScope();
251
});
252
253
// Development overrides
254
const developmentModule = new ContainerModule((bind, unbind, isBound, rebind) => {
255
rebind<ILogger>("Logger").to(VerboseLogger);
256
bind<IMockService>("MockService").to(MockService).inSingletonScope();
257
258
// Override email service with mock
259
if (isBound("EmailService")) {
260
rebind<IEmailService>("EmailService").to(MockEmailService);
261
} else {
262
bind<IEmailService>("EmailService").to(MockEmailService);
263
}
264
});
265
266
// Production overrides
267
const productionModule = new ContainerModule((bind, unbind, isBound, rebind) => {
268
rebind<ILogger>("Logger").to(WinstonLogger);
269
bind<IMetricsService>("MetricsService").to(PrometheusMetricsService).inSingletonScope();
270
bind<IEmailService>("EmailService").to(SendGridEmailService).inSingletonScope();
271
});
272
273
// Load based on environment
274
container.load(baseModule);
275
if (process.env.NODE_ENV === "production") {
276
container.load(productionModule);
277
} else {
278
container.load(developmentModule);
279
}
280
```
281
282
### Plugin Architecture
283
284
```typescript
285
interface IPlugin {
286
name: string;
287
initialize(): void;
288
destroy(): void;
289
}
290
291
// Plugin registry
292
const pluginRegistry = new Map<string, ContainerModule>();
293
294
// Register plugin
295
function registerPlugin(name: string, pluginModule: ContainerModule) {
296
pluginRegistry.set(name, pluginModule);
297
}
298
299
// Analytics plugin
300
const analyticsPlugin = new ContainerModule((bind) => {
301
bind<IPlugin>("Plugin").to(AnalyticsPlugin).whenTargetNamed("analytics");
302
bind<IAnalyticsService>("AnalyticsService").to(GoogleAnalyticsService).inSingletonScope();
303
});
304
305
registerPlugin("analytics", analyticsPlugin);
306
307
// Load enabled plugins
308
const enabledPlugins = process.env.ENABLED_PLUGINS?.split(",") || [];
309
for (const pluginName of enabledPlugins) {
310
const plugin = pluginRegistry.get(pluginName);
311
if (plugin) {
312
container.load(plugin);
313
}
314
}
315
```
316
317
### Testing Module Overrides
318
319
```typescript
320
// Main application modules
321
const appModule = new ContainerModule((bind) => {
322
bind<IUserService>("UserService").to(UserService);
323
bind<IEmailService>("EmailService").to(SmtpEmailService);
324
bind<IPaymentService>("PaymentService").to(StripePaymentService);
325
});
326
327
// Test overrides module
328
const testModule = new ContainerModule((bind, unbind, isBound, rebind) => {
329
// Override with mock implementations
330
rebind<IEmailService>("EmailService").to(MockEmailService);
331
rebind<IPaymentService>("PaymentService").to(MockPaymentService);
332
333
// Add test-specific services
334
bind<ITestDataService>("TestDataService").to(TestDataService).inSingletonScope();
335
});
336
337
// In test setup
338
container.load(appModule);
339
container.load(testModule); // Overrides production services
340
```
341
342
### Module Dependencies
343
344
```typescript
345
// Core module - must be loaded first
346
const coreModule = new ContainerModule((bind) => {
347
bind<ILogger>("Logger").to(WinstonLogger).inSingletonScope();
348
bind<IConfigService>("ConfigService").to(ConfigService).inSingletonScope();
349
});
350
351
// Database module - depends on core
352
const databaseModule = new ContainerModule((bind, unbind, isBound) => {
353
if (!isBound("ConfigService")) {
354
throw new Error("Database module requires core module to be loaded first");
355
}
356
357
bind<IDatabaseConnection>("DatabaseConnection")
358
.toDynamicValue((context) => {
359
const config = context.container.get<IConfigService>("ConfigService");
360
return new DatabaseConnection(config.getDatabaseUrl());
361
})
362
.inSingletonScope();
363
});
364
365
// Proper loading order
366
container.load(coreModule); // Load core first
367
container.load(databaseModule); // Then database module
368
```
369
370
## Module Best Practices
371
372
### Module Organization
373
374
```typescript
375
// Group related services together
376
const userModule = new ContainerModule((bind) => {
377
// User domain services
378
bind<IUserService>("UserService").to(UserService);
379
bind<IUserRepository>("UserRepository").to(UserRepository);
380
bind<IUserValidator>("UserValidator").to(UserValidator);
381
bind<IPasswordHasher>("PasswordHasher").to(BcryptHasher);
382
});
383
384
// Keep modules focused and cohesive
385
const emailModule = new ContainerModule((bind) => {
386
bind<IEmailService>("EmailService").to(EmailService);
387
bind<IEmailTemplateService>("EmailTemplateService").to(EmailTemplateService);
388
bind<IEmailQueueService>("EmailQueueService").to(EmailQueueService);
389
});
390
```
391
392
### Module Documentation
393
394
```typescript
395
/**
396
* Authentication Module
397
*
398
* Provides all services related to user authentication and authorization.
399
*
400
* Services provided:
401
* - IAuthService: Main authentication service
402
* - ITokenService: JWT token management
403
* - IPasswordService: Password hashing and validation
404
* - IUserRepository: User data persistence
405
*
406
* Dependencies:
407
* - Requires ConfigService from core module
408
* - Requires Logger from infrastructure module
409
*
410
* @example
411
* ```typescript
412
* container.load(coreModule);
413
* container.load(infrastructureModule);
414
* container.load(authModule);
415
* ```
416
*/
417
const authModule = new ContainerModule((bind, unbind, isBound) => {
418
// Validate dependencies
419
if (!isBound("ConfigService")) {
420
throw new Error("Auth module requires ConfigService");
421
}
422
if (!isBound("Logger")) {
423
throw new Error("Auth module requires Logger");
424
}
425
426
bind<IAuthService>("AuthService").to(JwtAuthService).inSingletonScope();
427
bind<ITokenService>("TokenService").to(JwtTokenService).inSingletonScope();
428
bind<IPasswordService>("PasswordService").to(BcryptPasswordService).inSingletonScope();
429
bind<IUserRepository>("UserRepository").to(DatabaseUserRepository).inSingletonScope();
430
});
431
```
432
433
## Best Practices
434
435
1. **Keep modules focused**: Each module should have a single responsibility
436
2. **Document dependencies**: Clearly specify what modules depend on others
437
3. **Validate dependencies**: Check required services are bound before adding new bindings
438
4. **Use environment modules**: Create environment-specific override modules
439
5. **Load order matters**: Load dependency modules before dependent modules
440
6. **Test module isolation**: Ensure modules can be loaded/unloaded independently
441
7. **Use descriptive names**: Module names should clearly indicate their purpose
442
8. **Handle cleanup**: Implement proper cleanup in deactivation handlers for module services