CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-inversify

A powerful and lightweight inversion of control container for JavaScript and Node.js apps powered by TypeScript.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

modules.mddocs/

Module System

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.

ContainerModule Class

class ContainerModule {
  constructor(
    registry: (
      bind: BindFunction, 
      unbind: UnbindFunction, 
      isBound: IsBoundFunction, 
      rebind: RebindFunction
    ) => void
  );
}

type BindFunction = <T>(serviceIdentifier: ServiceIdentifier<T>) => BindInFluentSyntax<T>;
type UnbindFunction = (serviceIdentifier: ServiceIdentifier) => void;
type IsBoundFunction = (serviceIdentifier: ServiceIdentifier) => boolean;
type RebindFunction = <T>(serviceIdentifier: ServiceIdentifier<T>) => BindInFluentSyntax<T>;

Module Loading Options

interface ContainerModuleLoadOptions {
  skipDeactivation?: boolean;
}

Basic Module Creation

Simple Module

import { ContainerModule } from "inversify";

// Create a module for authentication services
const authModule = new ContainerModule((bind) => {
  bind<IAuthService>("AuthService").to(JwtAuthService).inSingletonScope();
  bind<ITokenService>("TokenService").to(JwtTokenService).inSingletonScope();
  bind<IPasswordService>("PasswordService").to(BcryptPasswordService).inSingletonScope();
  bind<IUserRepository>("UserRepository").to(DatabaseUserRepository).inSingletonScope();
});

// Load the module
container.load(authModule);

Module with All Registry Functions

const databaseModule = new ContainerModule((bind, unbind, isBound, rebind) => {
  // Conditional binding
  if (!isBound("DatabaseConnection")) {
    bind<IDatabaseConnection>("DatabaseConnection")
      .to(PostgreSQLConnection)
      .inSingletonScope();
  }
  
  // Repository bindings
  bind<IUserRepository>("UserRepository").to(DatabaseUserRepository);
  bind<IProductRepository>("ProductRepository").to(DatabaseProductRepository);
  bind<IOrderRepository>("OrderRepository").to(DatabaseOrderRepository);
  
  // Rebind if testing environment
  if (process.env.NODE_ENV === "test") {
    rebind<IDatabaseConnection>("DatabaseConnection").to(InMemoryConnection);
  }
  
  // Clean up any existing cache bindings
  if (isBound("Cache")) {
    unbind("Cache");
  }
  
  bind<ICache>("Cache").to(RedisCache).inSingletonScope();
});

Module Categories and Examples

Infrastructure Module

const infrastructureModule = new ContainerModule((bind) => {
  // Logging
  bind<ILogger>("Logger").to(WinstonLogger).inSingletonScope();
  
  // Configuration
  bind<IConfigService>("ConfigService").to(EnvironmentConfigService).inSingletonScope();
  
  // Caching
  bind<ICache>("Cache").to(RedisCache).inSingletonScope();
  
  // Message Queue
  bind<IMessageQueue>("MessageQueue").to(RabbitMQService).inSingletonScope();
  
  // Health Monitoring
  bind<IHealthCheckService>("HealthCheck").to(HealthCheckService).inSingletonScope();
});

Business Logic Module

const businessModule = new ContainerModule((bind) => {
  // Domain Services
  bind<IUserService>("UserService").to(UserService);
  bind<IProductService>("ProductService").to(ProductService);
  bind<IOrderService>("OrderService").to(OrderService);
  bind<IPaymentService>("PaymentService").to(PaymentService);
  
  // Business Rules
  bind<IPricingEngine>("PricingEngine").to(DynamicPricingEngine).inSingletonScope();
  bind<IInventoryManager>("InventoryManager").to(InventoryManager).inSingletonScope();
  bind<INotificationService>("NotificationService").to(EmailNotificationService);
});

Data Access Module

const dataModule = new ContainerModule((bind) => {
  // Database Connection
  bind<IDatabaseConnection>("DatabaseConnection")
    .toDynamicValue(() => {
      const config = container.get<IConfigService>("ConfigService");
      return new PostgreSQLConnection(config.getDatabaseUrl());
    })
    .inSingletonScope();
  
  // Repositories
  bind<IUserRepository>("UserRepository").to(UserRepository);
  bind<IProductRepository>("ProductRepository").to(ProductRepository);
  bind<IOrderRepository>("OrderRepository").to(OrderRepository);
  
  // Query Builders
  bind<IQueryBuilder>("QueryBuilder").to(SqlQueryBuilder);
  
  // Migrations
  bind<IMigrationService>("MigrationService").to(MigrationService);
});

External Services Module

const externalServicesModule = new ContainerModule((bind) => {
  // Payment Processors
  bind<IPaymentProcessor>("PaymentProcessor")
    .to(StripePaymentProcessor)
    .whenTargetTagged("provider", "stripe");
  
  bind<IPaymentProcessor>("PaymentProcessor")
    .to(PayPalPaymentProcessor)
    .whenTargetTagged("provider", "paypal");
  
  // Email Service
  bind<IEmailService>("EmailService").to(SendGridEmailService).inSingletonScope();
  
  // SMS Service
  bind<ISmsService>("SmsService").to(TwilioSmsService).inSingletonScope();
  
  // File Storage
  bind<IFileStorage>("FileStorage").to(S3FileStorage).inSingletonScope();
  
  // Analytics
  bind<IAnalyticsService>("AnalyticsService").to(GoogleAnalyticsService).inSingletonScope();
});

Module Loading and Management

Loading Multiple Modules

// Load multiple modules at once
container.load(
  infrastructureModule,
  dataModule,
  businessModule,
  externalServicesModule
);

// Load modules sequentially
container.load(infrastructureModule);
container.load(dataModule);
container.load(businessModule);

Async Module Loading

// Load modules asynchronously
await container.loadAsync(
  infrastructureModule,
  dataModule,
  businessModule
);

// Individual async loading
await container.loadAsync(infrastructureModule);
await container.loadAsync(dataModule);

Module Unloading

// Unload specific modules
container.unload(externalServicesModule);

// Unload multiple modules
container.unload(businessModule, dataModule);

// Async unloading
await container.unloadAsync(externalServicesModule);

Conditional Module Loading

// Environment-based module loading
if (process.env.NODE_ENV === "production") {
  container.load(productionModule);
} else if (process.env.NODE_ENV === "test") {
  container.load(testModule);
} else {
  container.load(developmentModule);
}

// Feature-based module loading
if (process.env.FEATURE_ANALYTICS === "enabled") {
  container.load(analyticsModule);
}

if (process.env.FEATURE_PREMIUM === "enabled") {
  container.load(premiumFeaturesModule);
}

Advanced Module Patterns

Environment-Specific Modules

// Base module with common bindings
const baseModule = new ContainerModule((bind) => {
  bind<ILogger>("Logger").to(ConsoleLogger);
  bind<IConfigService>("ConfigService").to(ConfigService).inSingletonScope();
});

// Development overrides
const developmentModule = new ContainerModule((bind, unbind, isBound, rebind) => {
  rebind<ILogger>("Logger").to(VerboseLogger);
  bind<IMockService>("MockService").to(MockService).inSingletonScope();
  
  // Override email service with mock
  if (isBound("EmailService")) {
    rebind<IEmailService>("EmailService").to(MockEmailService);
  } else {
    bind<IEmailService>("EmailService").to(MockEmailService);
  }
});

// Production overrides
const productionModule = new ContainerModule((bind, unbind, isBound, rebind) => {
  rebind<ILogger>("Logger").to(WinstonLogger);
  bind<IMetricsService>("MetricsService").to(PrometheusMetricsService).inSingletonScope();
  bind<IEmailService>("EmailService").to(SendGridEmailService).inSingletonScope();
});

// Load based on environment
container.load(baseModule);
if (process.env.NODE_ENV === "production") {
  container.load(productionModule);
} else {
  container.load(developmentModule);
}

Plugin Architecture

interface IPlugin {
  name: string;
  initialize(): void;
  destroy(): void;
}

// Plugin registry
const pluginRegistry = new Map<string, ContainerModule>();

// Register plugin
function registerPlugin(name: string, pluginModule: ContainerModule) {
  pluginRegistry.set(name, pluginModule);
}

// Analytics plugin
const analyticsPlugin = new ContainerModule((bind) => {
  bind<IPlugin>("Plugin").to(AnalyticsPlugin).whenTargetNamed("analytics");
  bind<IAnalyticsService>("AnalyticsService").to(GoogleAnalyticsService).inSingletonScope();
});

registerPlugin("analytics", analyticsPlugin);

// Load enabled plugins
const enabledPlugins = process.env.ENABLED_PLUGINS?.split(",") || [];
for (const pluginName of enabledPlugins) {
  const plugin = pluginRegistry.get(pluginName);
  if (plugin) {
    container.load(plugin);
  }
}

Testing Module Overrides

// Main application modules
const appModule = new ContainerModule((bind) => {
  bind<IUserService>("UserService").to(UserService);
  bind<IEmailService>("EmailService").to(SmtpEmailService);
  bind<IPaymentService>("PaymentService").to(StripePaymentService);
});

// Test overrides module
const testModule = new ContainerModule((bind, unbind, isBound, rebind) => {
  // Override with mock implementations
  rebind<IEmailService>("EmailService").to(MockEmailService);
  rebind<IPaymentService>("PaymentService").to(MockPaymentService);
  
  // Add test-specific services
  bind<ITestDataService>("TestDataService").to(TestDataService).inSingletonScope();
});

// In test setup
container.load(appModule);
container.load(testModule); // Overrides production services

Module Dependencies

// Core module - must be loaded first
const coreModule = new ContainerModule((bind) => {
  bind<ILogger>("Logger").to(WinstonLogger).inSingletonScope();
  bind<IConfigService>("ConfigService").to(ConfigService).inSingletonScope();
});

// Database module - depends on core
const databaseModule = new ContainerModule((bind, unbind, isBound) => {
  if (!isBound("ConfigService")) {
    throw new Error("Database module requires core module to be loaded first");
  }
  
  bind<IDatabaseConnection>("DatabaseConnection")
    .toDynamicValue((context) => {
      const config = context.container.get<IConfigService>("ConfigService");
      return new DatabaseConnection(config.getDatabaseUrl());
    })
    .inSingletonScope();
});

// Proper loading order
container.load(coreModule);      // Load core first
container.load(databaseModule);  // Then database module

Module Best Practices

Module Organization

// Group related services together
const userModule = new ContainerModule((bind) => {
  // User domain services
  bind<IUserService>("UserService").to(UserService);
  bind<IUserRepository>("UserRepository").to(UserRepository);
  bind<IUserValidator>("UserValidator").to(UserValidator);
  bind<IPasswordHasher>("PasswordHasher").to(BcryptHasher);
});

// Keep modules focused and cohesive
const emailModule = new ContainerModule((bind) => {
  bind<IEmailService>("EmailService").to(EmailService);
  bind<IEmailTemplateService>("EmailTemplateService").to(EmailTemplateService);
  bind<IEmailQueueService>("EmailQueueService").to(EmailQueueService);
});

Module Documentation

/**
 * Authentication Module
 * 
 * Provides all services related to user authentication and authorization.
 * 
 * Services provided:
 * - IAuthService: Main authentication service
 * - ITokenService: JWT token management
 * - IPasswordService: Password hashing and validation
 * - IUserRepository: User data persistence
 * 
 * Dependencies:
 * - Requires ConfigService from core module
 * - Requires Logger from infrastructure module
 * 
 * @example
 * ```typescript
 * container.load(coreModule);
 * container.load(infrastructureModule);
 * container.load(authModule);
 * ```
 */
const authModule = new ContainerModule((bind, unbind, isBound) => {
  // Validate dependencies
  if (!isBound("ConfigService")) {
    throw new Error("Auth module requires ConfigService");
  }
  if (!isBound("Logger")) {
    throw new Error("Auth module requires Logger");
  }
  
  bind<IAuthService>("AuthService").to(JwtAuthService).inSingletonScope();
  bind<ITokenService>("TokenService").to(JwtTokenService).inSingletonScope();
  bind<IPasswordService>("PasswordService").to(BcryptPasswordService).inSingletonScope();
  bind<IUserRepository>("UserRepository").to(DatabaseUserRepository).inSingletonScope();
});

Best Practices

  1. Keep modules focused: Each module should have a single responsibility
  2. Document dependencies: Clearly specify what modules depend on others
  3. Validate dependencies: Check required services are bound before adding new bindings
  4. Use environment modules: Create environment-specific override modules
  5. Load order matters: Load dependency modules before dependent modules
  6. Test module isolation: Ensure modules can be loaded/unloaded independently
  7. Use descriptive names: Module names should clearly indicate their purpose
  8. Handle cleanup: Implement proper cleanup in deactivation handlers for module services

docs

binding.md

conditional.md

container.md

decorators.md

index.md

lifecycle.md

modules.md

tile.json