refactoring to remove a lot of junk
This commit is contained in:
parent
5318158e59
commit
d858222af7
33 changed files with 505 additions and 367 deletions
157
libs/LIBRARY_STANDARDS.md
Normal file
157
libs/LIBRARY_STANDARDS.md
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
# Library Standards and Patterns
|
||||
|
||||
This document defines the standardized patterns for all libraries in the @stock-bot ecosystem.
|
||||
|
||||
## Export Patterns
|
||||
|
||||
### Standard: Named Exports Only
|
||||
|
||||
All libraries should use **named exports only**. Default exports have been removed for consistency and better tree-shaking.
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
// ✅ Good - Named exports
|
||||
export { createCache } from './cache';
|
||||
export type { CacheOptions } from './types';
|
||||
|
||||
// ❌ Bad - Default export
|
||||
export default createCache;
|
||||
```
|
||||
|
||||
## Initialization Patterns
|
||||
|
||||
Libraries follow different initialization patterns based on their purpose:
|
||||
|
||||
### 1. Singleton with Global State
|
||||
**Use for:** Global services that should have only one instance (config, logger)
|
||||
|
||||
**Example:** Config library
|
||||
```typescript
|
||||
let configInstance: ConfigManager | null = null;
|
||||
|
||||
export function initializeConfig(): AppConfig {
|
||||
if (!configInstance) {
|
||||
configInstance = new ConfigManager();
|
||||
}
|
||||
return configInstance.initialize();
|
||||
}
|
||||
|
||||
export function getConfig(): AppConfig {
|
||||
if (!configInstance) {
|
||||
throw new Error('Config not initialized');
|
||||
}
|
||||
return configInstance.get();
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Factory with Registry
|
||||
**Use for:** Services that need instance reuse based on configuration (cache, logger instances)
|
||||
|
||||
**Example:** Cache library
|
||||
```typescript
|
||||
const cacheInstances = new Map<string, CacheProvider>();
|
||||
|
||||
export function createCache(options: CacheOptions): CacheProvider {
|
||||
if (options.shared) {
|
||||
const key = generateKey(options);
|
||||
if (cacheInstances.has(key)) {
|
||||
return cacheInstances.get(key)!;
|
||||
}
|
||||
const cache = new RedisCache(options);
|
||||
cacheInstances.set(key, cache);
|
||||
return cache;
|
||||
}
|
||||
return new RedisCache(options);
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Pure Factory Functions
|
||||
**Use for:** Services that need creation logic beyond simple instantiation
|
||||
|
||||
**Example:** Event bus with configuration processing
|
||||
```typescript
|
||||
export function createEventBus(config: EventBusConfig): EventBus {
|
||||
// Process config, set defaults, etc.
|
||||
const processedConfig = { ...defaultConfig, ...config };
|
||||
return new EventBus(processedConfig);
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** Simple instantiation doesn't need factories - use direct class instantiation or DI container.
|
||||
|
||||
### 4. Direct Class Exports
|
||||
**Use for:** Simple utilities or services managed by DI container
|
||||
|
||||
**Example:** MongoDB library
|
||||
```typescript
|
||||
export { MongoDBClient } from './client';
|
||||
// No factory function - let DI container handle instantiation
|
||||
```
|
||||
|
||||
### 5. Singleton Classes
|
||||
**Use for:** Manager classes that coordinate multiple instances
|
||||
|
||||
**Example:** QueueManager
|
||||
```typescript
|
||||
export class QueueManager {
|
||||
private static instance: QueueManager | null = null;
|
||||
|
||||
static initialize(config: QueueConfig): QueueManager {
|
||||
if (!QueueManager.instance) {
|
||||
QueueManager.instance = new QueueManager(config);
|
||||
}
|
||||
return QueueManager.instance;
|
||||
}
|
||||
|
||||
static getInstance(): QueueManager {
|
||||
if (!QueueManager.instance) {
|
||||
throw new Error('QueueManager not initialized');
|
||||
}
|
||||
return QueueManager.instance;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Pattern Selection Guide
|
||||
|
||||
Choose the initialization pattern based on these criteria:
|
||||
|
||||
| Pattern | When to Use | Examples |
|
||||
|---------|-------------|----------|
|
||||
| **Singleton with Global State** | - One instance per process<br>- Stateful configuration<br>- Process-wide settings | config, logger setup |
|
||||
| **Factory with Registry** | - Multiple instances with same config should share<br>- Connection pooling<br>- Resource optimization | cache, logger instances |
|
||||
| **Pure Factory** | - Complex initialization logic<br>- Configuration processing needed<br>- Defaults to apply | event bus (if needed) |
|
||||
| **Direct Class Export** | - DI container manages lifecycle<br>- Simple initialization<br>- No special setup needed | database clients (MongoDB, PostgreSQL, QuestDB), utilities |
|
||||
| **Singleton Class** | - Coordinates multiple resources<br>- Central management point<br>- Graceful shutdown needed | QueueManager, ConnectionManager |
|
||||
|
||||
## Additional Standards
|
||||
|
||||
### Error Handling
|
||||
- All libraries should throw descriptive errors
|
||||
- Consider creating custom error classes for domain-specific errors
|
||||
- Always include context in error messages
|
||||
|
||||
### Configuration
|
||||
- Accept configuration through constructor/factory parameters
|
||||
- Validate configuration using Zod schemas
|
||||
- Provide sensible defaults where appropriate
|
||||
|
||||
### Testing
|
||||
- All libraries must have unit tests
|
||||
- Use consistent test file naming: `*.test.ts`
|
||||
- Mock external dependencies
|
||||
|
||||
### Documentation
|
||||
- Every library must have a README.md
|
||||
- Include usage examples
|
||||
- Document all public APIs with JSDoc
|
||||
|
||||
### TypeScript
|
||||
- Export all public types
|
||||
- Use strict TypeScript settings
|
||||
- Avoid `any` types
|
||||
|
||||
### Dependencies
|
||||
- Minimize external dependencies
|
||||
- Use exact versions for critical dependencies
|
||||
- Document peer dependencies clearly
|
||||
|
|
@ -8,9 +8,9 @@ import { createCache, type CacheProvider } from '@stock-bot/cache';
|
|||
import type { IServiceContainer } from '@stock-bot/handlers';
|
||||
import { getLogger, type Logger } from '@stock-bot/logger';
|
||||
import { MongoDBClient } from '@stock-bot/mongodb';
|
||||
import { createPostgreSQLClient, type PostgreSQLClient } from '@stock-bot/postgres';
|
||||
import { PostgreSQLClient } from '@stock-bot/postgres';
|
||||
import { ProxyManager } from '@stock-bot/proxy';
|
||||
import { createQuestDBClient, type QuestDBClient } from '@stock-bot/questdb';
|
||||
import { QuestDBClient } from '@stock-bot/questdb';
|
||||
import { type QueueManager } from '@stock-bot/queue';
|
||||
import { asFunction, asValue, createContainer, InjectionMode, type AwilixContainer } from 'awilix';
|
||||
import { z } from 'zod';
|
||||
|
|
@ -146,7 +146,7 @@ export function createServiceContainer(rawConfig: unknown): AwilixContainer<Serv
|
|||
// Conditionally register PostgreSQL client
|
||||
if (config.postgres?.enabled !== false) {
|
||||
registrations.postgresClient = asFunction(({ postgresConfig, logger }) => {
|
||||
return createPostgreSQLClient(
|
||||
return new PostgreSQLClient(
|
||||
{
|
||||
host: postgresConfig.host,
|
||||
port: postgresConfig.port,
|
||||
|
|
@ -165,7 +165,7 @@ export function createServiceContainer(rawConfig: unknown): AwilixContainer<Serv
|
|||
if (config.questdb?.enabled !== false) {
|
||||
registrations.questdbClient = asFunction(({ questdbConfig, logger }) => {
|
||||
console.log('Creating QuestDB client with config:', questdbConfig);
|
||||
return createQuestDBClient(
|
||||
return new QuestDBClient(
|
||||
{
|
||||
host: questdbConfig.host,
|
||||
httpPort: questdbConfig.httpPort,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { getLogger } from '@stock-bot/logger';
|
||||
import { createJobHandler, handlerRegistry, type HandlerConfigWithSchedule } from '@stock-bot/types';
|
||||
import { fetch } from '@stock-bot/utils';
|
||||
import type { Collection } from 'mongodb';
|
||||
import type { IServiceContainer } from '../types/service-container';
|
||||
import type { ExecutionContext, IHandler } from '../types/types';
|
||||
import type { Collection } from 'mongodb';
|
||||
|
||||
/**
|
||||
* Abstract base class for all handlers with improved DI
|
||||
|
|
@ -196,7 +196,7 @@ export abstract class BaseHandler implements IHandler {
|
|||
*/
|
||||
protected hasService(name: keyof IServiceContainer): boolean {
|
||||
const service = this[name as keyof this];
|
||||
return service != null;
|
||||
return service !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -9,6 +9,3 @@ export { Logger, getLogger, shutdownLoggers, setLoggerConfig } from './logger';
|
|||
|
||||
// Type definitions
|
||||
export type { LogLevel, LogContext, LogMetadata, LoggerConfig } from './types';
|
||||
|
||||
// Default export
|
||||
export { getLogger as default } from './logger';
|
||||
|
|
|
|||
3
libs/data/cache/src/index.ts
vendored
3
libs/data/cache/src/index.ts
vendored
|
|
@ -45,6 +45,3 @@ export type {
|
|||
export { RedisConnectionManager } from './connection-manager';
|
||||
export { CacheKeyGenerator } from './key-generator';
|
||||
export { RedisCache } from './redis-cache';
|
||||
|
||||
// Default export for convenience
|
||||
export default createCache;
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
// This factory is no longer needed when using Awilix DI
|
||||
// The MongoDBClient is now registered directly in the DI container
|
||||
// See: libs/core/di/src/awilix-container.ts
|
||||
|
||||
export { MongoDBClient } from './client';
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
import { PostgreSQLClient } from './client';
|
||||
import type { PostgreSQLClientConfig, PostgreSQLConnectionOptions, ConnectionEvents } from './types';
|
||||
|
||||
/**
|
||||
* Factory function to create a PostgreSQL client instance
|
||||
*/
|
||||
export function createPostgreSQLClient(
|
||||
config: PostgreSQLClientConfig,
|
||||
logger?: any,
|
||||
options?: PostgreSQLConnectionOptions,
|
||||
events?: ConnectionEvents
|
||||
): PostgreSQLClient {
|
||||
return new PostgreSQLClient(config, logger, options, events);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and connect a PostgreSQL client
|
||||
*/
|
||||
export async function createAndConnectPostgreSQLClient(
|
||||
config: PostgreSQLClientConfig,
|
||||
logger?: any,
|
||||
options?: PostgreSQLConnectionOptions,
|
||||
events?: ConnectionEvents
|
||||
): Promise<PostgreSQLClient> {
|
||||
const client = createPostgreSQLClient(config, logger, options, events);
|
||||
await client.connect();
|
||||
return client;
|
||||
}
|
||||
|
||||
|
|
@ -33,10 +33,5 @@ export type {
|
|||
DynamicPoolConfig,
|
||||
} from './types';
|
||||
|
||||
// Factory functions
|
||||
export {
|
||||
createPostgreSQLClient,
|
||||
createAndConnectPostgreSQLClient,
|
||||
} from './factory';
|
||||
|
||||
// Singleton pattern removed - use factory functions instead
|
||||
// Note: Factory functions removed - instantiate directly with new PostgreSQLClient()
|
||||
// or use the Awilix DI container (recommended)
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
import { QuestDBClient } from './client';
|
||||
import type { QuestDBClientConfig, QuestDBConnectionOptions } from './types';
|
||||
|
||||
/**
|
||||
* Factory function to create a QuestDB client instance
|
||||
*/
|
||||
export function createQuestDBClient(
|
||||
config: QuestDBClientConfig,
|
||||
logger?: any,
|
||||
options?: QuestDBConnectionOptions
|
||||
): QuestDBClient {
|
||||
return new QuestDBClient(config, logger, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and connect a QuestDB client
|
||||
*/
|
||||
export async function createAndConnectQuestDBClient(
|
||||
config: QuestDBClientConfig,
|
||||
logger?: any,
|
||||
options?: QuestDBConnectionOptions
|
||||
): Promise<QuestDBClient> {
|
||||
const client = createQuestDBClient(config, logger, options);
|
||||
await client.connect();
|
||||
return client;
|
||||
}
|
||||
|
|
@ -28,5 +28,5 @@ export type {
|
|||
InsertResult,
|
||||
} from './types';
|
||||
|
||||
// Utils
|
||||
export { createQuestDBClient, createAndConnectQuestDBClient } from './factory';
|
||||
// Note: Factory functions removed - instantiate directly with new QuestDBClient()
|
||||
// or use the Awilix DI container (recommended)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
import { afterEach, describe, expect, it } from 'bun:test';
|
||||
import {
|
||||
createQuestDBClient,
|
||||
QuestDBClient,
|
||||
QuestDBHealthMonitor,
|
||||
QuestDBInfluxWriter,
|
||||
|
|
@ -40,9 +39,17 @@ describe('QuestDB Client Integration', () => {
|
|||
});
|
||||
|
||||
describe('Client Initialization', () => {
|
||||
it('should create client with factory function', () => {
|
||||
const factoryClient = createQuestDBClient();
|
||||
expect(factoryClient).toBeInstanceOf(QuestDBClient);
|
||||
it('should create client with constructor', () => {
|
||||
const newClient = new QuestDBClient({
|
||||
host: 'localhost',
|
||||
httpPort: 9000,
|
||||
pgPort: 8812,
|
||||
influxPort: 9009,
|
||||
database: 'questdb',
|
||||
user: 'admin',
|
||||
password: 'quest',
|
||||
});
|
||||
expect(newClient).toBeInstanceOf(QuestDBClient);
|
||||
});
|
||||
|
||||
it('should initialize all supporting classes', () => {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,4 @@ export function createEventBus(config: EventBusConfig): EventBus {
|
|||
|
||||
// Re-export everything
|
||||
export { EventBus } from './event-bus';
|
||||
export * from './types';
|
||||
|
||||
// Default export
|
||||
export default createEventBus;
|
||||
export * from './types';
|
||||
Loading…
Add table
Add a link
Reference in a new issue