# 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(); 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
- Stateful configuration
- Process-wide settings | config, logger setup | | **Factory with Registry** | - Multiple instances with same config should share
- Connection pooling
- Resource optimization | cache, logger instances | | **Pure Factory** | - Complex initialization logic
- Configuration processing needed
- Defaults to apply | event bus (if needed) | | **Direct Class Export** | - DI container manages lifecycle
- Simple initialization
- No special setup needed | database clients (MongoDB, PostgreSQL, QuestDB), utilities | | **Singleton Class** | - Coordinates multiple resources
- Central management point
- 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