removed configuration from index files and moved to di container
This commit is contained in:
parent
a3459f5865
commit
80c1dcb6cb
6 changed files with 261 additions and 94 deletions
142
DI-CONTAINER-SIMPLIFICATION.md
Normal file
142
DI-CONTAINER-SIMPLIFICATION.md
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
# 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)
|
||||
```typescript
|
||||
// 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)
|
||||
```typescript
|
||||
// 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
|
||||
```typescript
|
||||
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
|
||||
```typescript
|
||||
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
|
||||
```typescript
|
||||
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:
|
||||
```typescript
|
||||
// Before
|
||||
import { createServiceContainer, ... } from '@stock-bot/di';
|
||||
|
||||
// After
|
||||
import { createServiceContainerFromConfig, ... } from '@stock-bot/di';
|
||||
```
|
||||
|
||||
2. Replace the config mapping:
|
||||
```typescript
|
||||
// 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
|
||||
|
|
@ -9,7 +9,7 @@ import { Hono } from 'hono';
|
|||
import { cors } from 'hono/cors';
|
||||
// Library imports
|
||||
import {
|
||||
createServiceContainer,
|
||||
createServiceContainerFromConfig,
|
||||
initializeServices as initializeAwilixServices,
|
||||
type ServiceContainer,
|
||||
} from '@stock-bot/di';
|
||||
|
|
@ -50,40 +50,17 @@ async function initializeServices() {
|
|||
logger.info('Initializing data-ingestion service with improved DI...');
|
||||
|
||||
try {
|
||||
// Create Awilix container with proper config structure
|
||||
// Create Awilix container directly from AppConfig
|
||||
logger.debug('Creating Awilix DI container...');
|
||||
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, // Disable QuestDB for now
|
||||
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,
|
||||
},
|
||||
proxy: {
|
||||
cachePrefix: 'proxy:',
|
||||
ttl: 3600,
|
||||
},
|
||||
};
|
||||
|
||||
container = createServiceContainer(awilixConfig);
|
||||
container = createServiceContainerFromConfig(config, {
|
||||
enableQuestDB: false, // Data ingestion doesn't need QuestDB yet
|
||||
enableMongoDB: true,
|
||||
enablePostgres: true,
|
||||
enableCache: true,
|
||||
enableQueue: true,
|
||||
enableBrowser: true, // Data ingestion needs browser for web scraping
|
||||
enableProxy: true, // Data ingestion needs proxy for rate limiting
|
||||
});
|
||||
await initializeAwilixServices(container);
|
||||
logger.info('Awilix container created and initialized');
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { initializeServiceConfig } from '@stock-bot/config';
|
|||
|
||||
// Library imports
|
||||
import {
|
||||
createServiceContainer,
|
||||
createServiceContainerFromConfig,
|
||||
initializeServices as initializeAwilixServices,
|
||||
type ServiceContainer
|
||||
} from '@stock-bot/di';
|
||||
|
|
@ -53,36 +53,18 @@ async function initializeServices() {
|
|||
logger.info('Initializing data pipeline service with DI...');
|
||||
|
||||
try {
|
||||
// Create Awilix container with proper config structure
|
||||
// Create Awilix container directly from AppConfig
|
||||
logger.debug('Creating Awilix DI container...');
|
||||
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: config.database.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);
|
||||
container = createServiceContainerFromConfig(config, {
|
||||
enableQuestDB: config.database.questdb?.enabled || false,
|
||||
// Data pipeline needs all databases
|
||||
enableMongoDB: true,
|
||||
enablePostgres: true,
|
||||
enableCache: true,
|
||||
enableQueue: true,
|
||||
enableBrowser: false, // Data pipeline doesn't need browser
|
||||
enableProxy: false, // Data pipeline doesn't need proxy
|
||||
});
|
||||
await initializeAwilixServices(container);
|
||||
logger.info('Awilix container created and initialized');
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { initializeServiceConfig } from '@stock-bot/config';
|
|||
|
||||
// Library imports
|
||||
import {
|
||||
createServiceContainer,
|
||||
createServiceContainerFromConfig,
|
||||
initializeServices as initializeAwilixServices,
|
||||
type ServiceContainer
|
||||
} from '@stock-bot/di';
|
||||
|
|
@ -51,36 +51,17 @@ async function initializeServices() {
|
|||
logger.info('Initializing web API service with DI...');
|
||||
|
||||
try {
|
||||
// Create Awilix container with proper config structure
|
||||
// Create Awilix container directly from AppConfig
|
||||
logger.debug('Creating Awilix DI container...');
|
||||
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, // Web API doesn't need QuestDB
|
||||
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);
|
||||
container = createServiceContainerFromConfig(config, {
|
||||
enableQuestDB: false, // Web API doesn't need QuestDB
|
||||
enableMongoDB: true,
|
||||
enablePostgres: true,
|
||||
enableCache: true,
|
||||
enableQueue: false, // Web API doesn't need queue processing
|
||||
enableBrowser: false, // Web API doesn't need browser
|
||||
enableProxy: false, // Web API doesn't need proxy
|
||||
});
|
||||
await initializeAwilixServices(container);
|
||||
logger.info('Awilix container created and initialized');
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import { PostgreSQLClient } from '@stock-bot/postgres';
|
|||
import { ProxyManager } from '@stock-bot/proxy';
|
||||
import { QuestDBClient } from '@stock-bot/questdb';
|
||||
import { type QueueManager } from '@stock-bot/queue';
|
||||
import type { AppConfig as StockBotAppConfig } from '@stock-bot/config';
|
||||
|
||||
// Configuration schema with validation
|
||||
const appConfigSchema = z.object({
|
||||
|
|
@ -299,3 +300,85 @@ export async function initializeServices(container: AwilixContainer): Promise<vo
|
|||
// Export typed container
|
||||
export type ServiceContainer = AwilixContainer<ServiceDefinitions>;
|
||||
export type ServiceCradle = ServiceDefinitions;
|
||||
|
||||
/**
|
||||
* Service-specific options for container creation
|
||||
*/
|
||||
export interface ServiceContainerOptions {
|
||||
enableQuestDB?: boolean;
|
||||
enableMongoDB?: boolean;
|
||||
enablePostgres?: boolean;
|
||||
enableCache?: boolean;
|
||||
enableQueue?: boolean;
|
||||
enableBrowser?: boolean;
|
||||
enableProxy?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create service container directly from AppConfig
|
||||
* This eliminates the need for manual config mapping in each service
|
||||
*/
|
||||
export function createServiceContainerFromConfig(
|
||||
appConfig: StockBotAppConfig,
|
||||
options: ServiceContainerOptions = {}
|
||||
): AwilixContainer<ServiceDefinitions> {
|
||||
// Apply defaults for options
|
||||
const {
|
||||
enableQuestDB = true,
|
||||
enableMongoDB = true,
|
||||
enablePostgres = true,
|
||||
enableCache = true,
|
||||
enableQueue = true,
|
||||
enableBrowser = true,
|
||||
enableProxy = true,
|
||||
} = options;
|
||||
|
||||
// Build the config object expected by createServiceContainer
|
||||
const containerConfig = {
|
||||
redis: {
|
||||
enabled: enableCache && appConfig.database?.dragonfly ? true : false,
|
||||
host: appConfig.database?.dragonfly?.host || 'localhost',
|
||||
port: appConfig.database?.dragonfly?.port || 6379,
|
||||
password: appConfig.database?.dragonfly?.password,
|
||||
db: appConfig.database?.dragonfly?.db || 0,
|
||||
},
|
||||
mongodb: {
|
||||
enabled: enableMongoDB && appConfig.database?.mongodb ? true : false,
|
||||
uri: appConfig.database?.mongodb?.uri ||
|
||||
`mongodb://${appConfig.database?.mongodb?.user || ''}:${appConfig.database?.mongodb?.password || ''}@${appConfig.database?.mongodb?.host || 'localhost'}:${appConfig.database?.mongodb?.port || 27017}/${appConfig.database?.mongodb?.database || 'test'}?authSource=${appConfig.database?.mongodb?.authSource || 'admin'}`,
|
||||
database: appConfig.database?.mongodb?.database || 'test',
|
||||
},
|
||||
postgres: {
|
||||
enabled: enablePostgres && appConfig.database?.postgres ? true : false,
|
||||
host: appConfig.database?.postgres?.host || 'localhost',
|
||||
port: appConfig.database?.postgres?.port || 5432,
|
||||
database: appConfig.database?.postgres?.database || 'test',
|
||||
user: appConfig.database?.postgres?.user || 'test',
|
||||
password: appConfig.database?.postgres?.password || 'test',
|
||||
},
|
||||
questdb: enableQuestDB && appConfig.database?.questdb ? {
|
||||
enabled: true,
|
||||
host: appConfig.database.questdb.host || 'localhost',
|
||||
httpPort: appConfig.database.questdb.httpPort || 9000,
|
||||
pgPort: appConfig.database.questdb.pgPort || 8812,
|
||||
influxPort: appConfig.database.questdb.ilpPort || 9009,
|
||||
database: appConfig.database.questdb.database || 'questdb',
|
||||
} : {
|
||||
enabled: false,
|
||||
host: 'localhost',
|
||||
httpPort: 9000,
|
||||
pgPort: 8812,
|
||||
influxPort: 9009,
|
||||
},
|
||||
proxy: enableProxy ? {
|
||||
cachePrefix: 'proxy:',
|
||||
ttl: 3600,
|
||||
} : undefined,
|
||||
browser: enableBrowser ? {
|
||||
headless: true,
|
||||
timeout: 30000,
|
||||
} : undefined,
|
||||
};
|
||||
|
||||
return createServiceContainer(containerConfig);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,10 @@ export * from './types';
|
|||
// Awilix container exports
|
||||
export {
|
||||
createServiceContainer,
|
||||
createServiceContainerFromConfig,
|
||||
initializeServices,
|
||||
type AppConfig,
|
||||
type ServiceCradle,
|
||||
type ServiceContainer,
|
||||
type ServiceContainerOptions,
|
||||
} from './awilix-container';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue