refactoring continuing

This commit is contained in:
Boki 2025-06-22 08:27:54 -04:00
parent 742e590382
commit a0a3b26177
20 changed files with 394 additions and 798 deletions

View file

@ -5,7 +5,6 @@
import type { IServiceContainer } from '@stock-bot/handlers';
import type { IDataIngestionServices } from '../service-interfaces';
import { ProxyManager } from '@stock-bot/proxy';
/**
* Adapter that converts IDataIngestionServices to IServiceContainer
@ -23,9 +22,10 @@ export class DataIngestionServiceAdapter implements IServiceContainer {
// HTTP client not in current data services - will be added when needed
return null;
}
get proxy() {
// Return singleton proxy manager instance
return ProxyManager.getInstance();
get proxy(): any {
// Proxy manager should be injected via Awilix container
// This adapter is for legacy compatibility
throw new Error('ProxyManager must be provided through Awilix container');
}
// Database clients

View file

@ -0,0 +1,174 @@
/**
* Awilix DI Container Setup
* Creates a decoupled, reusable dependency injection container
*/
import { createContainer, asFunction, asValue, InjectionMode, type AwilixContainer } from 'awilix';
import { createCache, type CacheProvider } from '@stock-bot/cache';
import { ProxyManager } from '@stock-bot/proxy';
import { getLogger } from '@stock-bot/logger';
import type { IServiceContainer } from '@stock-bot/handlers';
// Configuration types
export interface AppConfig {
redis: {
host: string;
port: number;
password?: string;
username?: string;
db?: number;
};
mongodb: {
uri: string;
database: string;
};
postgres: {
host: string;
port: number;
database: string;
user: string;
password: string;
};
questdb?: {
host: string;
port: number;
};
proxy?: {
cachePrefix?: string;
ttl?: number;
};
}
/**
* Create and configure the DI container
*/
export function createServiceContainer(config: AppConfig): AwilixContainer {
const container = createContainer({
injectionMode: InjectionMode.PROXY,
});
// Register configuration values
container.register({
// Configuration
config: asValue(config),
redisConfig: asValue(config.redis),
mongoConfig: asValue(config.mongodb),
postgresConfig: asValue(config.postgres),
questdbConfig: asValue(config.questdb || { host: 'localhost', port: 9009 }),
// Core services with dependency injection
logger: asFunction(() => getLogger('app')).singleton(),
// Cache with injected config and logger
cache: asFunction(({ redisConfig, logger }) =>
createCache({
redisConfig,
logger,
keyPrefix: 'cache:',
ttl: 3600,
enableMetrics: true,
})
).singleton(),
// Proxy manager with injected cache and logger
proxyManager: asFunction(({ cache, config, logger }) => {
const manager = new ProxyManager(
cache,
config.proxy || {},
logger
);
// Note: initialization happens in initializeServices function
return manager;
}).singleton(),
// HTTP client can be added here when decoupled
httpClient: asFunction(() => {
// TODO: Import and create HTTP client when decoupled
return null;
}).singleton(),
// Database clients - placeholders for now
mongoClient: asFunction(() => {
// TODO: Create MongoDB client
return null;
}).singleton(),
postgresClient: asFunction(() => {
// TODO: Create PostgreSQL client
return null;
}).singleton(),
questdbClient: asFunction(() => {
// TODO: Create QuestDB client
return null;
}).singleton(),
// Queue manager - placeholder
queueManager: asFunction(() => {
// TODO: Create queue manager when decoupled
return null;
}).singleton(),
// Build the IServiceContainer for handlers
serviceContainer: asFunction((cradle) => ({
logger: cradle.logger,
cache: cradle.cache,
proxy: cradle.proxyManager,
http: cradle.httpClient,
mongodb: cradle.mongoClient,
postgres: cradle.postgresClient,
questdb: cradle.questdbClient,
queue: cradle.queueManager,
} as IServiceContainer)).singleton(),
});
return container;
}
/**
* Initialize async services after container creation
*/
export async function initializeServices(container: AwilixContainer): Promise<void> {
const logger = container.resolve('logger');
try {
// Wait for cache to be ready first
const cache = container.resolve('cache');
if (cache && typeof cache.waitForReady === 'function') {
await cache.waitForReady(10000);
logger.info('Cache is ready');
}
// Initialize proxy manager
const proxyManager = container.resolve('proxyManager');
if (proxyManager && typeof proxyManager.initialize === 'function') {
await proxyManager.initialize();
logger.info('Proxy manager initialized');
}
// Initialize other async services as needed
// ...
logger.info('All services initialized successfully');
} catch (error) {
logger.error('Failed to initialize services', { error });
throw error;
}
}
// Type definitions for container resolution
export interface ServiceCradle {
config: AppConfig;
logger: any;
cache: CacheProvider;
proxyManager: ProxyManager;
httpClient: any;
mongoClient: any;
postgresClient: any;
questdbClient: any;
queueManager: any;
serviceContainer: IServiceContainer;
}
// Export typed container
export type ServiceContainer = AwilixContainer<ServiceCradle>;

View file

@ -6,4 +6,13 @@ export * from './pool-size-calculator';
export * from './types';
export * from './service-interfaces';
export * from './service-factory';
export * from './adapters/service-adapter';
export * from './adapters/service-adapter';
// Awilix container exports
export {
createServiceContainer,
initializeServices,
type AppConfig,
type ServiceCradle,
type ServiceContainer
} from './awilix-container';

View file

@ -5,7 +5,6 @@
import { getLogger } from '@stock-bot/logger';
import { ConnectionFactory } from './connection-factory';
import { PoolSizeCalculator } from './pool-size-calculator';
import { ProxyManager } from '@stock-bot/proxy';
import type {
IDataIngestionServices,
IServiceFactory,
@ -45,9 +44,7 @@ export class DataIngestionServiceFactory implements IServiceFactory {
this.createQueueConnection(connectionFactory, config)
]);
// Initialize proxy manager
logger.info('Initializing proxy manager...');
await ProxyManager.initialize();
// Note: Proxy manager initialization moved to Awilix container
const services: IDataIngestionServices = {
mongodb: mongoPool.client,