@logistically/events-nestjs
Version:
NestJS integration for @logistically/events v3 - Event-driven architecture with Redis Streams
477 lines (351 loc) • 12.2 kB
Markdown
# @logistically/events-nestjs
A NestJS integration library for the `@logistically/events` event system, providing integration with NestJS applications including automatic event handler discovery, decorators, and services.
## Features
- **NestJS Integration** - Designed for NestJS with decorators and dependency injection
- **Automatic Event Handler Discovery** - Automatically discovers and registers event handlers using decorators
- **Performance Optimizations** - Intelligent caching, batch processing, and performance monitoring
- **Multiple Transport Support** - Redis Streams, Memory, and custom transport plugins
- **Pattern-Based Event Routing** - Flexible event routing with wildcard support
- **Type Safety** - Full TypeScript support with proper typing
- **Minimal Configuration** - Minimal setup required with sensible defaults
- **Global Module Support** - Can be configured as a global module for app-wide access
## Installation
```bash
npm install @logistically/events-nestjs @logistically/events
```
## Quick Start
### 1. Import the Module
```typescript
import { Module } from '@nestjs/common';
import { EventsModule } from '@logistically/events-nestjs';
@Module({
imports: [
EventsModule.forRoot({
service: 'my-app',
autoDiscovery: true,
global: true
})
]
})
export class AppModule {}
```
### 2. Create Event Handlers
```typescript
import { Injectable } from '@nestjs/common';
import { AutoEvents, AutoEventHandler, NestJSEvent } from '@logistically/events-nestjs';
@Injectable()
@AutoEvents()
export class UserService {
@AutoEventHandler({ eventType: 'user.created' })
async handleUserCreated(event: NestJSEvent<{ id: number; email: string }>) {
console.log('User created:', event.body);
}
@AutoEventHandler({ eventType: 'order.*' })
async handleAllOrderEvents(event: NestJSEvent<any>) {
console.log('Order event received:', event.body);
}
}
```
### 3. Publish Events
```typescript
import { Injectable } from '@nestjs/common';
import { EventPublisherService } from '@logistically/events-nestjs';
@Injectable()
export class OrderService {
constructor(private readonly eventPublisher: EventPublisherService) {}
async createOrder(orderData: any) {
// Your business logic here
// Publish event
await this.eventPublisher.publish('order.created', orderData);
}
}
```
That's it! Your event handlers will be automatically discovered and registered.
## Core Concepts
### Event System
This library integrates with the `@logistically/events` core library, which provides:
- **Event Publishing** - Publish events to multiple transports
- **Event Consumption** - Consume events with pattern matching
- **Transport Plugins** - Redis Streams, Memory, and custom transports
- **Event Routing** - Pattern-based routing between transports
- **Batching & Performance** - Configurable batching strategies
- **Reliability** - Dead letter queues, retries, and error handling
For detailed information about the core event system, see [@logistically/events](https://github.com/onwello/events/).
### NestJS Integration
The library provides NestJS-specific features:
- **Module Integration** - `EventsModule` for easy setup
- **Service Injection** - Injectable services for publishing and consuming
- **Decorator Support** - Decorators for automatic handler registration
- **Dependency Injection** - Full NestJS DI integration
- **Lifecycle Hooks** - Integration with NestJS lifecycle
## Configuration
### Basic Configuration
```typescript
EventsModule.forRoot({
service: 'my-app', // Service name for event routing
originPrefix: 'my-app', // Origin prefix for events
autoDiscovery: true, // Enable automatic handler discovery
global: true // Make module globally available
})
```
### Advanced Configuration
```typescript
import { RedisStreamsPlugin, MemoryTransportPlugin } from '@logistically/events';
EventsModule.forRoot({
service: 'my-app',
originPrefix: 'my-app',
autoDiscovery: true,
global: true,
// Transport configuration
transports: new Map([
['redis', new RedisStreamsPlugin().createTransport({
url: 'redis://localhost:6379',
groupId: 'my-app-group'
})],
['memory', new MemoryTransportPlugin().createTransport()]
]),
// Event routing
routing: {
routes: [
{ pattern: 'user.*', transport: 'redis' },
{ pattern: 'order.*', transport: 'redis' },
{ pattern: 'system.*', transport: 'memory' }
]
},
// Publisher configuration
publisher: {
batching: {
enabled: true,
maxSize: 1000,
maxWaitMs: 100
}
},
// Consumer configuration
consumer: {
enablePatternRouting: true,
enableConsumerGroups: true
}
})
```
For complete configuration options, see [@logistically/events Configuration](https://github.com/onwello/events/).
## API Reference
### Decorators
#### `@AutoEvents(options?)`
Simple decorator for automatic event handler discovery.
```typescript
@AutoEvents({ enabled: true, priority: 0 })
export class MyService {}
```
**Options:**
- `enabled` (boolean): Enable/disable auto-discovery for this service
- `priority` (number): Priority for handler registration order
#### `@AutoEventHandler(options)`
Marks a method as an event handler.
```typescript
@AutoEventHandler({ eventType: 'user.created' })
async handleUserCreated(event: NestJSEvent<User>) {
// Handle event
}
```
**Options:**
- `eventType` (string): Event type pattern (supports wildcards like `user.*`)
- `priority` (number): Handler priority within the same event type
- `async` (boolean): Whether the handler is async
- `retry` (object): Retry configuration
### Services
#### `EventPublisherService`
Publishes events to the event system.
```typescript
constructor(private readonly eventPublisher: EventPublisherService) {}
// Publish single event
await this.eventPublisher.publish('user.created', userData);
// Publish batch
await this.eventPublisher.publishBatch('user.created', users);
```
#### `EventConsumerService`
Consumes events from the event system.
```typescript
constructor(private readonly eventConsumer: EventConsumerService) {}
// Subscribe to specific event
await this.eventConsumer.subscribe('user.created', handler);
// Subscribe to pattern
await this.eventConsumer.subscribePattern('user.*', handler);
```
#### `EventSystemService`
Manages the core event system.
```typescript
constructor(private readonly eventSystem: EventSystemService) {}
// Get system status
const status = await this.eventSystem.getStatus();
// Check connection
const connected = this.eventSystem.isConnected();
```
#### `EventDiscoveryService`
Manages automatic event handler discovery and registration.
```typescript
constructor(private readonly eventDiscoveryService: EventDiscoveryService) {}
// Manual registration
const count = await this.eventDiscoveryService.registerEventHandlers(this);
```
### Event Object
#### `NestJSEvent<T>`
The event object passed to handlers. This extends the core `EventEnvelope<T>` from `@logistically/events`.
```typescript
interface NestJSEvent<T = any> extends EventEnvelope<T> {
nestjsMetadata?: NestJSEventMetadata;
}
interface NestJSEventMetadata {
correlationId?: string;
causationId?: string;
[key: string]: any;
}
```
The event object inherits all properties from `EventEnvelope<T>` which includes:
- `body: T` - The event payload data
- `header` - Event metadata (id, type, origin, timestamp, etc.)
- `nestjsMetadata` - Optional NestJS-specific metadata
## Transport Plugins
### Redis Streams
```typescript
import { RedisStreamsPlugin } from '@logistically/events';
const redisTransport = new RedisStreamsPlugin().createTransport({
url: 'redis://localhost:6379',
groupId: 'my-app-group',
batchSize: 100,
enableDLQ: true,
maxRetries: 3
});
```
### Memory Transport
```typescript
import { MemoryTransportPlugin } from '@logistically/events';
const memoryTransport = new MemoryTransportPlugin().createTransport();
```
For complete transport configuration options, see [@logistically/events Transports](https://github.com/onwello/events/).
## Event Routing
### Pattern-Based Routing
```typescript
routing: {
routes: [
{ pattern: 'user.*', transport: 'redis' }, // All user events to Redis
{ pattern: 'order.created', transport: 'redis' }, // Specific event to Redis
{ pattern: 'system.*', transport: 'memory' } // System events to memory
]
}
```
### Wildcard Support
- `user.*` - All user events
- `order.*.created` - Order events ending with 'created'
- `*.notification` - All notification events
For advanced routing configuration, see [@logistically/events Routing](https://github.com/onwello/events/).
## Error Handling
### Handler Error Handling
```typescript
@AutoEventHandler({ eventType: 'user.created' })
async handleUserCreated(event: NestJSEvent<User>) {
try {
// Your logic here
} catch (error) {
// Handle errors - they won't break the event system
console.error('Handler error:', error);
}
}
```
### Dead Letter Queue (Redis)
```typescript
const redisTransport = new RedisStreamsPlugin().createTransport({
enableDLQ: true,
dlqStreamPrefix: 'dlq:',
maxRetries: 3
});
```
For comprehensive error handling strategies, see [@logistically/events Error Handling](https://github.com/onwello/events/).
## Testing
### Unit Testing
```typescript
import { Test } from '@nestjs/testing';
import { EventsModule } from '@logistically/events-nestjs';
describe('UserService', () => {
let service: UserService;
beforeEach(async () => {
const module = await Test.createTestingModule({
imports: [
EventsModule.forRoot({
service: 'test',
autoDiscovery: true
})
],
providers: [UserService]
}).compile();
service = module.get<UserService>(UserService);
});
it('should handle user created events', async () => {
// Test your event handler
});
});
```
### Integration Testing
```typescript
import { Test } from '@nestjs/testing';
import { EventsModule } from '@logistically/events-nestjs';
describe('Event Integration', () => {
let app: INestApplication;
beforeEach(async () => {
const module = await Test.createTestingModule({
imports: [
EventsModule.forRoot({
service: 'test',
autoDiscovery: true,
transports: new Map([
['memory', new MemoryTransportPlugin().createTransport()]
])
})
]
}).compile();
app = module.createNestApplication();
await app.init();
});
afterEach(async () => {
await app.close();
});
});
```
## Troubleshooting
### Common Issues
#### Handlers Not Being Registered
1. Ensure `autoDiscovery: true` is set in module configuration
2. Verify the service has the `@AutoEvents()` decorator
3. Check that `EventDiscoveryService` is injected into the service
4. Verify event handler methods have the `@AutoEventHandler()` decorator
#### Events Not Being Received
1. Check transport configuration
2. Verify event routing patterns
3. Ensure consumer is properly initialized
4. Check Redis connection (if using Redis transport)
### Debug Mode
Enable debug logging:
```typescript
EventsModule.forRoot({
service: 'my-app',
autoDiscovery: true,
debug: true // Enable debug logging
})
```
## Examples
See the `examples/` directory for complete working examples:
- **Basic Setup** - Simple event handler registration
- **Cross-Service Communication** - Services handling events from other services
- **Pattern-Based Routing** - Event routing between transports
- **Redis Integration** - Production-ready Redis Streams setup
## Contributing
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Add tests
5. Submit a pull request
## License
MIT License - see LICENSE file for details.
## Related Links
- [@logistically/events](https://github.com/onwello/events/) - Core events library with detailed configuration, features, and benchmarks
- [NestJS](https://nestjs.com/) - Progressive Node.js framework
- [Redis](https://redis.io/) - In-memory data structure store