stock-bot/DI-CONTAINER-SIMPLIFICATION.md

4.3 KiB
Raw Blame History

DI Container Configuration Simplification

Overview

We've simplified the dependency injection container setup across all backend services by creating a new createServiceContainerFromConfig function that directly accepts the AppConfig, eliminating repetitive configuration mapping code.

Before vs After

Before (30+ lines per service)

// In each service's index.ts
const awilixConfig = {
  redis: {
    host: config.database.dragonfly.host,
    port: config.database.dragonfly.port,
    db: config.database.dragonfly.db,
  },
  mongodb: {
    uri: config.database.mongodb.uri,
    database: config.database.mongodb.database,
  },
  postgres: {
    host: config.database.postgres.host,
    port: config.database.postgres.port,
    database: config.database.postgres.database,
    user: config.database.postgres.user,
    password: config.database.postgres.password,
  },
  questdb: {
    enabled: false,
    host: config.database.questdb.host,
    httpPort: config.database.questdb.httpPort,
    pgPort: config.database.questdb.pgPort,
    influxPort: config.database.questdb.ilpPort,
    database: config.database.questdb.database,
  },
};

container = createServiceContainer(awilixConfig);

After (2-3 lines per service)

// In each service's index.ts
container = createServiceContainerFromConfig(config, {
  enableQuestDB: false, // Service-specific options
  enableMongoDB: true,
  enablePostgres: true,
  // ... other options
});

Benefits

  1. Code Reduction: ~30 lines reduced to 2-3 lines per service
  2. Centralized Mapping: Configuration structure mapping is now in one place
  3. Service-Specific Control: Each service can enable/disable specific components
  4. Type Safety: Direct use of AppConfig ensures type safety
  5. Maintainability: Changes to config structure only need updates in one place

Service Configurations

Data Ingestion Service

container = createServiceContainerFromConfig(config, {
  enableQuestDB: false,   // Not needed yet
  enableMongoDB: true,    // Stores raw data
  enablePostgres: true,   // Stores metadata
  enableCache: true,      // For rate limiting
  enableQueue: true,      // Job processing
  enableBrowser: true,    // Web scraping
  enableProxy: true,      // Proxy rotation
});

Data Pipeline Service

container = createServiceContainerFromConfig(config, {
  enableQuestDB: config.database.questdb?.enabled || false,
  enableMongoDB: true,    // Reads raw data
  enablePostgres: true,   // Writes processed data
  enableCache: true,      // Query caching
  enableQueue: true,      // Job processing
  enableBrowser: false,   // Not needed
  enableProxy: false,     // Not needed
});

Web API Service

container = createServiceContainerFromConfig(config, {
  enableQuestDB: false,   // Not needed
  enableMongoDB: true,    // Reads data
  enablePostgres: true,   // Reads data
  enableCache: true,      // API caching
  enableQueue: false,     // No job processing
  enableBrowser: false,   // Not needed
  enableProxy: false,     // Not needed
});

Implementation Details

The new function in @stock-bot/di/awilix-container.ts:

  • Accepts the standard AppConfig from @stock-bot/config
  • Maps the nested config structure to what Awilix expects
  • Provides sensible defaults for all options
  • Only creates services that are enabled for the specific service

Migration Guide

To migrate a service:

  1. Change the import:

    // Before
    import { createServiceContainer, ... } from '@stock-bot/di';
    
    // After
    import { createServiceContainerFromConfig, ... } from '@stock-bot/di';
    
  2. Replace the config mapping:

    // Before
    const awilixConfig = { /* 30+ lines of mapping */ };
    container = createServiceContainer(awilixConfig);
    
    // After
    container = createServiceContainerFromConfig(config, {
      // Service-specific options
    });
    
  3. Choose which services to enable based on your service's needs

Result

  • 3 services updated: data-ingestion, data-pipeline, web-api
  • ~90 lines of code removed (30 lines × 3 services)
  • Cleaner, more maintainable codebase
  • Easier to add new services with minimal boilerplate