Event-Driven Architecture

Como projetar sistemas reativos onde os componentes se comunicam por meio de eventos, alcançando desacoplamento, escalabilidade e processamento assíncrono.

Que problema resolve

Em uma arquitetura tradicional baseada em chamadas síncronas (request-response), os serviços estão fortemente acoplados: o emissor precisa conhecer o receptor, esperar sua resposta e lidar com suas falhas. Isso gera diversos problemas:

  • Acoplamento temporal: O emissor fica bloqueado até que o receptor responda
  • Acoplamento de conhecimento: O emissor precisa saber quem processa sua solicitação
  • Escalabilidade limitada: Adicionar um novo consumidor exige modificar o emissor
  • Fragilidade: Se o receptor estiver fora do ar, o emissor também falha
Comunicação síncrona (acoplada):
  Order Service ──► Inventory Service (espera resposta)
                ──► Payment Service (espera resposta)
                ──► Notification Service (espera resposta)
                ──► Analytics Service (espera resposta)
  
  Order Service conhece TODOS os serviços
  Se um falha, o pedido falha
  Adicionar um serviço = modificar Order Service

Como funciona

A arquitetura orientada a eventos (EDA) inverte o modelo de comunicação. Em vez de um serviço chamar diretamente os outros, ele publica um evento que descreve algo que aconteceu. Os serviços interessados se inscrevem nesses eventos e reagem de forma independente.

Comunicação por eventos (desacoplada):
  Order Service ──evento──► Event Bus
                            "OrderCreated"

                    ┌───────────┼───────────┐
                    ▼           ▼           ▼
              Inventory    Payment    Notification
              Service      Service    Service
  
  Order Service NÃO conhece os consumidores
  Cada serviço reage de forma independente
  Adicionar um serviço = se inscrever no evento

Conceitos-chave

ConceitoDescriçãoExemplo
EventoRegistro imutável de algo que aconteceuOrderCreated, PaymentProcessed
ProdutorServiço que publica eventosOrder Service publica OrderCreated
ConsumidorServiço que reage a eventosInventory Service escuta OrderCreated
Event Bus/BrokerInfraestrutura que transporta eventosKafka, RabbitMQ, Amazon SNS/SQS
Topic/QueueCanal por onde os eventos fluemorders.created, payments.processed

Tipos de eventos

Domain Events (eventos de domínio): Representam fatos do negócio.

{
  "type": "OrderCreated",
  "timestamp": "2024-01-15T10:30:00Z",
  "data": {
    "orderId": "ORD-001",
    "customerId": "USR-123",
    "items": [{"productId": "P001", "quantity": 2}],
    "total": 150.00
  }
}

Integration Events (eventos de integração): Comunicam mudanças entre bounded contexts.

{
  "type": "InventoryReserved",
  "source": "inventory-service",
  "data": {
    "orderId": "ORD-001",
    "reservationId": "RES-456",
    "status": "confirmed"
  }
}

Padrões de entrega

Pub/Sub (Publish-Subscribe): Um evento é entregue a todos os assinantes.

Produtor ──► Topic ──► Consumidor A
                   ──► Consumidor B
                   ──► Consumidor C

Event Streaming: Os eventos são armazenados em um log persistente e os consumidores leem no seu próprio ritmo.

Produtor ──► Log persistente (Kafka)
              [evento1, evento2, evento3, ...]
              
  Consumidor A: lendo o evento 3 (em dia)
  Consumidor B: lendo o evento 1 (atrasado, processando)
  Consumidor C: novo, começa a partir do evento 1

Garantias de entrega

GarantiaDescriçãoUso
At-most-onceO evento é entregue no máximo uma vez (pode se perder)Métricas, analytics
At-least-onceO evento é entregue pelo menos uma vez (pode se duplicar)A maioria dos casos (com idempotência)
Exactly-onceO evento é entregue exatamente uma vezTransações financeiras (difícil de alcançar)

Vantagens

  • Desacoplamento: Produtores e consumidores não se conhecem
  • Escalabilidade: Adicionar consumidores não exige modificar produtores
  • Resiliência: Se um consumidor está fora do ar, os eventos se acumulam e são processados quando ele voltar
  • Processamento assíncrono: O produtor não espera os consumidores terminarem
  • Auditoria natural: Os eventos formam um log de tudo o que aconteceu no sistema
  • Extensibilidade: Novas funcionalidades são adicionadas se inscrevendo em eventos existentes

Trade-offs / Desvantagens

  • Consistência eventual: Os dados não são atualizados instantaneamente em todos os serviços
  • Complexidade de debugging: Rastrear o fluxo de um evento através de múltiplos serviços é difícil
  • Ordenação: Garantir a ordem de processamento dos eventos exige um projeto cuidadoso
  • Duplicatas: Os consumidores precisam ser idempotentes para lidar com eventos duplicados
  • Infraestrutura adicional: Você precisa de um broker de mensageria (Kafka, RabbitMQ) com sua operação
  • Testing complexo: Testar fluxos assíncronos é mais difícil do que testar chamadas síncronas

Quando usar

  • Sistemas onde múltiplos serviços precisam reagir ao mesmo evento
  • Quando o processamento pode ser assíncrono (você não precisa de resposta imediata)
  • Sistemas com requisitos de alta escalabilidade e throughput
  • Quando você precisa desacoplar serviços para que evoluam de forma independente
  • Fluxos de negócio complexos com múltiplas etapas (pedidos, pagamentos, envios)
  • Quando você precisa de um log de auditoria de tudo o que acontece no sistema

Quando evitar

  • Operações que exigem resposta síncrona imediata (consultas do usuário)
  • Sistemas simples com poucos serviços e fluxos lineares
  • Quando a consistência forte é um requisito não negociável
  • Times sem experiência em sistemas distribuídos e debugging assíncrono
  • Quando você não consegue justificar a infraestrutura de um broker de mensageria

Tecnologias e implementações comuns

CategoriaOpções
Event StreamingApache Kafka, Amazon Kinesis, Azure Event Hubs
Message BrokersRabbitMQ, Amazon SQS/SNS, Google Pub/Sub, NATS
Event StoreEventStoreDB, Kafka (como event store)
FrameworksSpring Cloud Stream, MassTransit (.NET), Axon Framework
ObservabilidadeOpenTelemetry, Jaeger, Zipkin (tracing distribuído)

Relação com outros padrões

  • Saga Pattern: Usa eventos para coordenar transações distribuídas entre serviços
  • Outbox Pattern: Garante a publicação confiável de eventos junto com mudanças no banco de dados
  • CQRS: Separa leituras e escritas usando eventos para sincronizar os modelos
  • Idempotência: Os consumidores precisam ser idempotentes para lidar com eventos duplicados
  • Circuit Breaker: Protege os consumidores de sobrecarga quando há picos de eventos

Próximos passos

A arquitetura orientada a eventos é a base da comunicação nos microsserviços modernos. Para proteger a integração com sistemas externos que não falam a sua linguagem de eventos, explore o Anti-Corruption Layer. Para garantir que os consumidores lidem corretamente com duplicatas, revise o padrão de Idempotência.