stock-bot/libs/LIBRARY_STANDARDS.md

157 lines
No EOL
4.9 KiB
Markdown

# 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