adding data-services
This commit is contained in:
parent
e3bfd05b90
commit
405b818c86
139 changed files with 55943 additions and 416 deletions
42
libs/event-bus/README.md
Normal file
42
libs/event-bus/README.md
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# Event Bus Library
|
||||
|
||||
A Redis-based event bus implementation for inter-service communication in the stock-bot project.
|
||||
|
||||
## Features
|
||||
|
||||
- Publish/subscribe pattern for asynchronous messaging
|
||||
- Support for typed events based on `@stock-bot/shared-types`
|
||||
- Reliable message delivery
|
||||
- Channel-based subscriptions
|
||||
|
||||
## Usage
|
||||
|
||||
```typescript
|
||||
import { createEventBus } from '@stock-bot/event-bus';
|
||||
import { MarketDataEvent } from '@stock-bot/shared-types';
|
||||
|
||||
// Create an event bus instance
|
||||
const eventBus = createEventBus({
|
||||
redisHost: 'localhost',
|
||||
redisPort: 6379
|
||||
});
|
||||
|
||||
// Subscribe to market data events
|
||||
eventBus.subscribe('market.data', async (event: MarketDataEvent) => {
|
||||
console.log(`Received price update for ${event.data.symbol}: ${event.data.price}`);
|
||||
});
|
||||
|
||||
// Publish an event
|
||||
await eventBus.publish('market.data', {
|
||||
type: 'MARKET_DATA',
|
||||
data: {
|
||||
symbol: 'AAPL',
|
||||
price: 150.25,
|
||||
bid: 150.20,
|
||||
ask: 150.30,
|
||||
volume: 1000000,
|
||||
timestamp: new Date()
|
||||
},
|
||||
timestamp: new Date()
|
||||
});
|
||||
```
|
||||
22
libs/event-bus/package.json
Normal file
22
libs/event-bus/package.json
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"name": "@stock-bot/event-bus",
|
||||
"version": "1.0.0",
|
||||
"description": "Event bus implementation for inter-service communication",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"dev": "tsc --watch",
|
||||
"clean": "rm -rf dist",
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@stock-bot/shared-types": "workspace:*",
|
||||
"ioredis": "^5.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.2",
|
||||
"jest": "^29.5.0",
|
||||
"typescript": "^5.4.5"
|
||||
}
|
||||
}
|
||||
131
libs/event-bus/src/EventBus.ts
Normal file
131
libs/event-bus/src/EventBus.ts
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
import Redis from 'ioredis';
|
||||
import { Event } from '@stock-bot/shared-types';
|
||||
|
||||
export type EventHandler<T extends Event = Event> = (event: T) => Promise<void> | void;
|
||||
|
||||
export interface EventBusConfig {
|
||||
redisHost: string;
|
||||
redisPort: number;
|
||||
redisPassword?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event Bus for publishing and subscribing to events in the system
|
||||
* Provides reliable message delivery and pattern-based subscriptions
|
||||
*/
|
||||
export class EventBus {
|
||||
private publisher: Redis;
|
||||
private subscriber: Redis;
|
||||
private handlers: Map<string, EventHandler[]>;
|
||||
private isConnected: boolean = false;
|
||||
|
||||
constructor(private config: EventBusConfig) {
|
||||
this.publisher = new Redis({
|
||||
host: config.redisHost,
|
||||
port: config.redisPort,
|
||||
password: config.redisPassword,
|
||||
});
|
||||
|
||||
this.subscriber = new Redis({
|
||||
host: config.redisHost,
|
||||
port: config.redisPort,
|
||||
password: config.redisPassword,
|
||||
});
|
||||
|
||||
this.handlers = new Map<string, EventHandler[]>();
|
||||
this.setupConnectionHandlers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up Redis connection event handlers
|
||||
*/
|
||||
private setupConnectionHandlers(): void {
|
||||
this.publisher.on('connect', () => {
|
||||
console.log('Publisher connected to Redis');
|
||||
this.isConnected = true;
|
||||
});
|
||||
|
||||
this.publisher.on('error', (err) => {
|
||||
console.error('Publisher Redis error', err);
|
||||
this.isConnected = false;
|
||||
});
|
||||
|
||||
this.subscriber.on('connect', () => {
|
||||
console.log('Subscriber connected to Redis');
|
||||
});
|
||||
|
||||
this.subscriber.on('error', (err) => {
|
||||
console.error('Subscriber Redis error', err);
|
||||
});
|
||||
|
||||
this.subscriber.on('message', (channel, message) => {
|
||||
try {
|
||||
const event = JSON.parse(message) as Event;
|
||||
this.processEvent(channel, event);
|
||||
} catch (err) {
|
||||
console.error('Error processing event', err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an incoming event by calling all registered handlers
|
||||
*/
|
||||
private processEvent(channel: string, event: Event): void {
|
||||
const handlers = this.handlers.get(channel) || [];
|
||||
|
||||
handlers.forEach(handler => {
|
||||
try {
|
||||
handler(event);
|
||||
} catch (err) {
|
||||
console.error(`Error in event handler for ${channel}:`, err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish an event to the specified channel
|
||||
*/
|
||||
public async publish(channel: string, event: Event): Promise<void> {
|
||||
if (!this.isConnected) {
|
||||
throw new Error('Not connected to Redis');
|
||||
}
|
||||
|
||||
await this.publisher.publish(channel, JSON.stringify(event));
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to events on a specific channel
|
||||
*/
|
||||
public subscribe(channel: string, handler: EventHandler): () => void {
|
||||
if (!this.handlers.has(channel)) {
|
||||
this.handlers.set(channel, []);
|
||||
this.subscriber.subscribe(channel);
|
||||
}
|
||||
|
||||
const handlers = this.handlers.get(channel)!;
|
||||
handlers.push(handler);
|
||||
|
||||
// Return unsubscribe function
|
||||
return () => {
|
||||
const index = handlers.indexOf(handler);
|
||||
if (index >= 0) {
|
||||
handlers.splice(index, 1);
|
||||
}
|
||||
|
||||
if (handlers.length === 0) {
|
||||
this.handlers.delete(channel);
|
||||
this.subscriber.unsubscribe(channel);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Close connections
|
||||
*/
|
||||
public async close(): Promise<void> {
|
||||
await this.publisher.quit();
|
||||
await this.subscriber.quit();
|
||||
this.isConnected = false;
|
||||
}
|
||||
}
|
||||
8
libs/event-bus/src/index.ts
Normal file
8
libs/event-bus/src/index.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import { EventBus, EventBusConfig, EventHandler } from './EventBus';
|
||||
|
||||
export { EventBus, EventBusConfig, EventHandler };
|
||||
|
||||
// Convenience function to create an event bus with the default configuration
|
||||
export function createEventBus(config: EventBusConfig): EventBus {
|
||||
return new EventBus(config);
|
||||
}
|
||||
13
libs/event-bus/tsconfig.json
Normal file
13
libs/event-bus/tsconfig.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"esModuleInterop": true,
|
||||
"declaration": true,
|
||||
"outDir": "dist",
|
||||
"strict": true,
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue