260223:1415 20260223 nextJS & nestJS Best pratices
All checks were successful
Build and Deploy / deploy (push) Successful in 4m44s
All checks were successful
Build and Deploy / deploy (push) Successful in 4m44s
This commit is contained in:
108
.agents/skills/nestjs-best-practices/rules/arch-use-events.md
Normal file
108
.agents/skills/nestjs-best-practices/rules/arch-use-events.md
Normal file
@@ -0,0 +1,108 @@
|
||||
---
|
||||
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)
|
||||
Reference in New Issue
Block a user