157 lines
No EOL
4.9 KiB
Markdown
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 |