Skip to content

Factory Pattern

Factory pattern allows you to create services with complex initialization logic.

Basic Factory

typescript
const httpClientFactory = (provider: ServiceProvider) => {
  const config = provider.getRequiredService<IConfig>(IConfigToken);
  return new HttpClient(config.apiUrl, config.timeout);
};

services.addSingleton<IHttpClient>(IHttpClientToken, httpClientFactory);

Async Factory

Factories can be async for asynchronous initialization:

typescript
const databaseFactory = async (provider: ServiceProvider) => {
  const connectionString = await provider.getRequiredService<string>(ConnectionStringToken);
  const db = new Database(connectionString);
  await db.connect(); // Async initialization
  return db;
};

services.addSingleton<IDatabase>(IDatabaseToken, databaseFactory);

Conditional Logic

Use factories to choose implementations based on configuration:

typescript
const loggerFactory = async (provider: ServiceProvider) => {
  const logType = await provider.getRequiredService<string>(LogTypeToken);
  if (logType === 'file') {
    return new FileLogger();
  }
  return new ConsoleLogger();
};

services.addSingleton<ILogger>(ILoggerToken, loggerFactory);

Complex Object Creation

Factories are perfect for creating objects with multiple dependencies:

typescript
const apiClientFactory = async (provider: ServiceProvider) => {
  const config = await provider.getRequiredService<IConfig>(IConfigToken);
  const apiKey = await provider.getRequiredService<string>(ApiKeyToken);
  return new ApiClient(config.apiUrl, config.timeout, apiKey);
};

services.addSingleton<IApiClient>(IApiClientToken, apiClientFactory);

Use Cases

  • Async initialization - Database connections, HTTP clients
  • Conditional logic - Environment-based implementations
  • Complex dependencies - Multiple configuration values
  • Dynamic creation - Runtime decision making

Next Steps

Released under the ISC License. If you find this project helpful, consider buying me a coffee