109 lines
3.0 KiB
Markdown
109 lines
3.0 KiB
Markdown
---
|
|
title: Use Event-Driven Architecture for Decoupling
|
|
impact: MEDIUM-HIGH
|
|
impactDescription: Enables async processing and modularity
|
|
tags: architecture, events, decoupling
|
|
---
|
|
|
|
## Use Event-Driven Architecture for Decoupling
|
|
|
|
Use `@nestjs/event-emitter` for intra-service events and message brokers for inter-service communication. Events allow modules to react to changes without direct dependencies, improving modularity and enabling async processing.
|
|
|
|
**Incorrect (direct service coupling):**
|
|
|
|
```typescript
|
|
// Direct service coupling
|
|
@Injectable()
|
|
export class OrdersService {
|
|
constructor(
|
|
private inventoryService: InventoryService,
|
|
private emailService: EmailService,
|
|
private analyticsService: AnalyticsService,
|
|
private notificationService: NotificationService,
|
|
private loyaltyService: LoyaltyService,
|
|
) {}
|
|
|
|
async createOrder(dto: CreateOrderDto): Promise<Order> {
|
|
const order = await this.repo.save(dto);
|
|
|
|
// Tight coupling - OrdersService knows about all consumers
|
|
await this.inventoryService.reserve(order.items);
|
|
await this.emailService.sendConfirmation(order);
|
|
await this.analyticsService.track('order_created', order);
|
|
await this.notificationService.push(order.userId, 'Order placed');
|
|
await this.loyaltyService.addPoints(order.userId, order.total);
|
|
|
|
// Adding new behavior requires modifying this service
|
|
return order;
|
|
}
|
|
}
|
|
```
|
|
|
|
**Correct (event-driven decoupling):**
|
|
|
|
```typescript
|
|
// Use EventEmitter for decoupling
|
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
|
|
|
// Define event
|
|
export class OrderCreatedEvent {
|
|
constructor(
|
|
public readonly orderId: string,
|
|
public readonly userId: string,
|
|
public readonly items: OrderItem[],
|
|
public readonly total: number,
|
|
) {}
|
|
}
|
|
|
|
// Service emits events
|
|
@Injectable()
|
|
export class OrdersService {
|
|
constructor(
|
|
private eventEmitter: EventEmitter2,
|
|
private repo: Repository<Order>,
|
|
) {}
|
|
|
|
async createOrder(dto: CreateOrderDto): Promise<Order> {
|
|
const order = await this.repo.save(dto);
|
|
|
|
// Emit event - no knowledge of consumers
|
|
this.eventEmitter.emit(
|
|
'order.created',
|
|
new OrderCreatedEvent(order.id, order.userId, order.items, order.total),
|
|
);
|
|
|
|
return order;
|
|
}
|
|
}
|
|
|
|
// Listeners in separate modules
|
|
@Injectable()
|
|
export class InventoryListener {
|
|
@OnEvent('order.created')
|
|
async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {
|
|
await this.inventoryService.reserve(event.items);
|
|
}
|
|
}
|
|
|
|
@Injectable()
|
|
export class EmailListener {
|
|
@OnEvent('order.created')
|
|
async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {
|
|
await this.emailService.sendConfirmation(event.orderId);
|
|
}
|
|
}
|
|
|
|
@Injectable()
|
|
export class AnalyticsListener {
|
|
@OnEvent('order.created')
|
|
async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {
|
|
await this.analyticsService.track('order_created', {
|
|
orderId: event.orderId,
|
|
total: event.total,
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
Reference: [NestJS Events](https://docs.nestjs.com/techniques/events)
|