Por que Event-Driven?
Em arquiteturas de microserviços, a comunicação síncrona (REST/gRPC) cria acoplamento temporal: se um serviço está indisponível, toda a cadeia falha. Arquiteturas event-driven resolvem isso através de comunicação assíncrona baseada em eventos.
Loose Coupling
Serviços não precisam conhecer uns aos outros diretamente
Escalabilidade
Consumidores podem ser escalados independentemente
Resiliência
Falhas são isoladas, eventos podem ser reprocessados
Temporalidade
Histórico completo de eventos para auditoria e replay
Padrões Fundamentais
1. Event Notification
O padrão mais simples: publicar eventos para notificar outros serviços de mudanças.
// Serviço de Orders publica evento
interface OrderCreatedEvent {
eventType: 'OrderCreated';
timestamp: Date;
data: {
orderId: string;
customerId: string;
items: Array<{ productId: string; quantity: number }>;
totalAmount: number;
};
}
// Producer
async function createOrder(order: Order): Promise<void> {
// Persiste a ordem
await orderRepository.save(order);
// Publica evento
await eventBus.publish('orders', {
eventType: 'OrderCreated',
timestamp: new Date(),
data: {
orderId: order.id,
customerId: order.customerId,
items: order.items,
totalAmount: order.totalAmount,
},
});
}
// Consumer (Serviço de Inventory)
eventBus.subscribe('orders', async (event: OrderCreatedEvent) => {
if (event.eventType === 'OrderCreated') {
for (const item of event.data.items) {
await inventoryService.reserveStock(item.productId, item.quantity);
}
}
});
2. Event-Carried State Transfer
Eventos carregam dados suficientes para que consumidores não precisem buscar mais informações.
// ❌ Evento pobre - força consumer a buscar dados
interface OrderCreatedPoor {
orderId: string; // Consumer precisa chamar API de Orders
}
// ✅ Evento rico - contém tudo que consumers precisam
interface OrderCreatedRich {
eventType: 'OrderCreated';
timestamp: Date;
data: {
orderId: string;
customerId: string;
customerEmail: string; // Para serviço de notificação
customerName: string;
shippingAddress: Address; // Para serviço de shipping
items: Array<{
productId: string;
productName: string;
quantity: number;
unitPrice: number;
}>;
totalAmount: number;
paymentMethod: string;
};
}
Eventos ricos reduzem chamadas entre serviços, mas aumentam o tamanho das mensagens e podem expor mais dados do que necessário.
3. Event Sourcing
Em vez de armazenar apenas o estado atual, armazena todos os eventos que levaram àquele estado.
// Event Store para Conta Bancária
interface AccountEvent {
eventId: string;
accountId: string;
eventType: string;
timestamp: Date;
data: unknown;
}
interface AccountOpened {
eventType: 'AccountOpened';
data: { initialBalance: number; ownerName: string };
}
interface MoneyDeposited {
eventType: 'MoneyDeposited';
data: { amount: number; description: string };
}
interface MoneyWithdrawn {
eventType: 'MoneyWithdrawn';
data: { amount: number; description: string };
}
// Reconstruir estado a partir de eventos
function rebuildAccount(events: AccountEvent[]): Account {
return events.reduce((account, event) => {
switch (event.eventType) {
case 'AccountOpened':
return {
id: event.accountId,
balance: event.data.initialBalance,
ownerName: event.data.ownerName,
isOpen: true,
};
case 'MoneyDeposited':
return { ...account, balance: account.balance + event.data.amount };
case 'MoneyWithdrawn':
return { ...account, balance: account.balance - event.data.amount };
case 'AccountClosed':
return { ...account, isOpen: false };
default:
return account;
}
}, {} as Account);
}
// Persistir novo evento
async function deposit(accountId: string, amount: number): Promise<void> {
const events = await eventStore.getEvents(accountId);
const account = rebuildAccount(events);
if (!account.isOpen) {
throw new Error('Account is closed');
}
await eventStore.append(accountId, {
eventType: 'MoneyDeposited',
data: { amount, description: 'Deposit' },
});
}
4. CQRS (Command Query Responsibility Segregation)
Separa o modelo de escrita (commands) do modelo de leitura (queries).
// Write Side - Domain Model rico
class Order {
private events: DomainEvent[] = [];
static create(customerId: string, items: OrderItem[]): Order {
const order = new Order();
order.apply(new OrderCreated(customerId, items));
return order;
}
addItem(productId: string, quantity: number): void {
this.apply(new ItemAdded(this.id, productId, quantity));
}
private apply(event: DomainEvent): void {
this.events.push(event);
this.when(event);
}
private when(event: DomainEvent): void {
// Atualiza estado interno
}
}
// Read Side - Projection otimizada para queries
interface OrderReadModel {
orderId: string;
customerName: string;
customerEmail: string;
status: string;
totalAmount: number;
itemCount: number;
createdAt: Date;
// Dados denormalizados para evitar joins
}
class OrderProjection {
async handle(event: DomainEvent): Promise<void> {
switch (event.type) {
case 'OrderCreated':
await this.db.insert('orders_read', {
orderId: event.orderId,
customerName: event.customerName,
status: 'created',
// ...
});
break;
case 'OrderShipped':
await this.db.update('orders_read', {
orderId: event.orderId,
status: 'shipped',
shippedAt: event.timestamp,
});
break;
}
}
}
Saga Pattern para Transações Distribuídas
Quando uma operação envolve múltiplos serviços, use Sagas para coordenar.
Choreography-based Saga
// Cada serviço reage a eventos e pode emitir compensações
class PaymentService {
@EventHandler('OrderCreated')
async handleOrderCreated(event: OrderCreatedEvent): Promise<void> {
try {
await this.processPayment(event.data.orderId, event.data.totalAmount);
await this.eventBus.publish('payments', {
eventType: 'PaymentCompleted',
data: { orderId: event.data.orderId },
});
} catch (error) {
await this.eventBus.publish('payments', {
eventType: 'PaymentFailed',
data: { orderId: event.data.orderId, reason: error.message },
});
}
}
}
class OrderService {
@EventHandler('PaymentFailed')
async handlePaymentFailed(event: PaymentFailedEvent): Promise<void> {
// Compensação: cancelar a ordem
await this.cancelOrder(event.data.orderId);
await this.eventBus.publish('orders', {
eventType: 'OrderCancelled',
data: { orderId: event.data.orderId, reason: 'Payment failed' },
});
}
}
Orchestration-based Saga
// Saga Orchestrator centraliza a lógica
class OrderSagaOrchestrator {
async execute(orderId: string): Promise<void> {
const saga = new Saga(orderId);
try {
// Step 1: Reserve inventory
saga.addStep({
execute: () => this.inventoryService.reserve(orderId),
compensate: () => this.inventoryService.release(orderId),
});
// Step 2: Process payment
saga.addStep({
execute: () => this.paymentService.charge(orderId),
compensate: () => this.paymentService.refund(orderId),
});
// Step 3: Create shipment
saga.addStep({
execute: () => this.shippingService.createShipment(orderId),
compensate: () => this.shippingService.cancelShipment(orderId),
});
await saga.run();
} catch (error) {
await saga.compensate();
throw error;
}
}
}
Choreography: Mais desacoplado, melhor para fluxos simples. Orchestration: Mais fácil de entender e debugar, melhor para fluxos complexos.
Message Brokers
Apache Kafka
# docker-compose.yaml
services:
kafka:
image: confluentinc/cp-kafka:7.5.0
environment:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
ports:
- "9092:9092"
// Producer
import { Kafka } from 'kafkajs';
const kafka = new Kafka({ brokers: ['kafka:9092'] });
const producer = kafka.producer();
await producer.connect();
await producer.send({
topic: 'orders',
messages: [
{
key: orderId, // Garante ordenação por order
value: JSON.stringify(event),
headers: {
'event-type': 'OrderCreated',
'correlation-id': correlationId,
},
},
],
});
// Consumer
const consumer = kafka.consumer({ groupId: 'inventory-service' });
await consumer.connect();
await consumer.subscribe({ topic: 'orders', fromBeginning: false });
await consumer.run({
eachMessage: async ({ topic, partition, message }) => {
const event = JSON.parse(message.value.toString());
await processEvent(event);
},
});
Boas Práticas
Quer implementar arquitetura event-driven nos seus microserviços? Fale com nossos especialistas em Software Engineering.