Files
agent-skills/skills/nestjs-best-practices/rules/micro-use-patterns.md
Jason Woltje 861b28b965 feat: Expand fleet to 23 skills across all domains
New skills (14):
- nestjs-best-practices: 40 priority-ranked rules (kadajett)
- fastapi: Pydantic v2, async SQLAlchemy, JWT auth (jezweb)
- architecture-patterns: Clean Architecture, Hexagonal, DDD (wshobson)
- python-performance-optimization: Profiling and optimization (wshobson)
- ai-sdk: Vercel AI SDK streaming and agent patterns (vercel)
- create-agent: Modular agent architecture with OpenRouter (openrouterteam)
- proactive-agent: WAL Protocol, compaction recovery, self-improvement (halthelobster)
- brand-guidelines: Brand identity enforcement (anthropics)
- ui-animation: Motion design with accessibility (mblode)
- marketing-ideas: 139 ideas across 14 categories (coreyhaines31)
- pricing-strategy: SaaS pricing and tier design (coreyhaines31)
- programmatic-seo: SEO at scale with playbooks (coreyhaines31)
- competitor-alternatives: Comparison page architecture (coreyhaines31)
- referral-program: Referral and affiliate programs (coreyhaines31)

README reorganized by domain: Code Quality, Frontend, Backend,
Auth, AI/Agent Building, Marketing, Design, Meta.

Mosaic Stack is not limited to coding — the Orchestrator serves
coding, business, design, marketing, writing, logistics, and analysis.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 16:22:53 -06:00

168 lines
5.0 KiB
Markdown

---
title: Use Message and Event Patterns Correctly
impact: MEDIUM
impactDescription: Proper patterns ensure reliable microservice communication
tags: microservices, message-pattern, event-pattern, communication
---
## Use Message and Event Patterns Correctly
NestJS microservices support two communication patterns: request-response (MessagePattern) and event-based (EventPattern). Use MessagePattern when you need a response, and EventPattern for fire-and-forget notifications. Understanding the difference prevents communication bugs.
**Incorrect (using wrong pattern for use case):**
```typescript
// Use @MessagePattern for fire-and-forget
@Controller()
export class NotificationsController {
@MessagePattern('user.created')
async handleUserCreated(data: UserCreatedEvent) {
// This WAITS for response, blocking the sender
await this.emailService.sendWelcome(data.email);
// If email fails, sender gets an error (coupling!)
}
}
// Use @EventPattern expecting a response
@Controller()
export class OrdersController {
@EventPattern('inventory.check')
async checkInventory(data: CheckInventoryDto) {
const available = await this.inventory.check(data);
return available; // This return value is IGNORED with @EventPattern!
}
}
// Tight coupling in client
@Injectable()
export class UsersService {
async createUser(dto: CreateUserDto): Promise<User> {
const user = await this.repo.save(dto);
// Blocks until notification service responds
await this.client.send('user.created', user).toPromise();
// If notification service is down, user creation fails!
return user;
}
}
```
**Correct (use MessagePattern for request-response, EventPattern for fire-and-forget):**
```typescript
// MessagePattern: Request-Response (when you NEED a response)
@Controller()
export class InventoryController {
@MessagePattern({ cmd: 'check_inventory' })
async checkInventory(data: CheckInventoryDto): Promise<InventoryResult> {
const result = await this.inventoryService.check(data.productId, data.quantity);
return result; // Response sent back to caller
}
}
// Client expects response
@Injectable()
export class OrdersService {
async createOrder(dto: CreateOrderDto): Promise<Order> {
// Check inventory - we NEED this response to proceed
const inventory = await firstValueFrom(
this.inventoryClient.send<InventoryResult>(
{ cmd: 'check_inventory' },
{ productId: dto.productId, quantity: dto.quantity },
),
);
if (!inventory.available) {
throw new BadRequestException('Insufficient inventory');
}
return this.repo.save(dto);
}
}
// EventPattern: Fire-and-Forget (for notifications, side effects)
@Controller()
export class NotificationsController {
@EventPattern('user.created')
async handleUserCreated(data: UserCreatedEvent): Promise<void> {
// No return value needed - just process the event
await this.emailService.sendWelcome(data.email);
await this.analyticsService.track('user_signup', data);
// If this fails, it doesn't affect the sender
}
}
// Client emits event without waiting
@Injectable()
export class UsersService {
async createUser(dto: CreateUserDto): Promise<User> {
const user = await this.repo.save(dto);
// Fire and forget - doesn't block, doesn't wait
this.eventClient.emit('user.created', {
userId: user.id,
email: user.email,
timestamp: new Date(),
});
return user; // User creation succeeds regardless of event handling
}
}
// Hybrid pattern for critical events
@Injectable()
export class OrdersService {
async createOrder(dto: CreateOrderDto): Promise<Order> {
const order = await this.repo.save(dto);
// Critical: inventory reservation (use MessagePattern)
const reserved = await firstValueFrom(
this.inventoryClient.send({ cmd: 'reserve_inventory' }, {
orderId: order.id,
items: dto.items,
}),
);
if (!reserved.success) {
await this.repo.delete(order.id);
throw new BadRequestException('Could not reserve inventory');
}
// Non-critical: notifications (use EventPattern)
this.eventClient.emit('order.created', {
orderId: order.id,
userId: dto.userId,
total: dto.total,
});
return order;
}
}
// Error handling patterns
// MessagePattern errors propagate to caller
@MessagePattern({ cmd: 'get_user' })
async getUser(userId: string): Promise<User> {
const user = await this.repo.findOne({ where: { id: userId } });
if (!user) {
throw new RpcException('User not found'); // Received by caller
}
return user;
}
// EventPattern errors should be handled locally
@EventPattern('order.created')
async handleOrderCreated(data: OrderCreatedEvent): Promise<void> {
try {
await this.processOrder(data);
} catch (error) {
// Log and potentially retry - don't throw
this.logger.error('Failed to process order event', error);
await this.deadLetterQueue.add(data);
}
}
```
Reference: [NestJS Microservices](https://docs.nestjs.com/microservices/basics)