Event-Driven Architecture

Cómo diseñar sistemas reactivos donde los componentes se comunican mediante eventos, logrando desacoplamiento, escalabilidad y procesamiento asíncrono.

Qué problema resuelve

En una arquitectura tradicional basada en llamadas síncronas (request-response), los servicios están fuertemente acoplados: el emisor debe conocer al receptor, esperar su respuesta y manejar sus fallos. Esto genera varios problemas:

  • Acoplamiento temporal: El emisor se bloquea hasta que el receptor responde
  • Acoplamiento de conocimiento: El emisor debe saber quién procesa su solicitud
  • Escalabilidad limitada: Agregar un nuevo consumidor requiere modificar el emisor
  • Fragilidad: Si el receptor está caído, el emisor también falla
Comunicación síncrona (acoplada):
  Order Service ──► Inventory Service (espera respuesta)
                ──► Payment Service (espera respuesta)
                ──► Notification Service (espera respuesta)
                ──► Analytics Service (espera respuesta)
  
  Order Service conoce a TODOS los servicios
  Si uno falla, el pedido falla
  Agregar un servicio = modificar Order Service

Cómo funciona

La arquitectura orientada a eventos (EDA) invierte el modelo de comunicación. En lugar de que un servicio llame directamente a otros, publica un evento que describe algo que ocurrió. Los servicios interesados se suscriben a esos eventos y reaccionan de forma independiente.

Comunicación por eventos (desacoplada):
  Order Service ──evento──► Event Bus
                            "OrderCreated"

                    ┌───────────┼───────────┐
                    ▼           ▼           ▼
              Inventory    Payment    Notification
              Service      Service    Service
  
  Order Service NO conoce a los consumidores
  Cada servicio reacciona de forma independiente
  Agregar un servicio = suscribirse al evento

Conceptos clave

ConceptoDescripciónEjemplo
EventoRegistro inmutable de algo que ocurrióOrderCreated, PaymentProcessed
ProductorServicio que publica eventosOrder Service publica OrderCreated
ConsumidorServicio que reacciona a eventosInventory Service escucha OrderCreated
Event Bus/BrokerInfraestructura que transporta eventosKafka, RabbitMQ, Amazon SNS/SQS
Topic/QueueCanal por donde fluyen los eventosorders.created, payments.processed

Tipos de eventos

Domain Events (eventos de dominio): Representan hechos del negocio.

{
  "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 integración): Comunican cambios entre bounded contexts.

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

Patrones de entrega

Pub/Sub (Publish-Subscribe): Un evento se entrega a todos los suscriptores.

Productor ──► Topic ──► Consumidor A
                   ──► Consumidor B
                   ──► Consumidor C

Event Streaming: Los eventos se almacenan en un log persistente y los consumidores leen a su propio ritmo.

Productor ──► Log persistente (Kafka)
              [evento1, evento2, evento3, ...]
              
  Consumidor A: leyendo evento 3 (al día)
  Consumidor B: leyendo evento 1 (atrasado, procesando)
  Consumidor C: nuevo, empieza desde evento 1

Garantías de entrega

GarantíaDescripciónUso
At-most-onceEl evento se entrega como máximo una vez (puede perderse)Métricas, analytics
At-least-onceEl evento se entrega al menos una vez (puede duplicarse)La mayoría de casos (con idempotencia)
Exactly-onceEl evento se entrega exactamente una vezTransacciones financieras (difícil de lograr)

Ventajas

  • Desacoplamiento: Productores y consumidores no se conocen entre sí
  • Escalabilidad: Agregar consumidores no requiere modificar productores
  • Resiliencia: Si un consumidor está caído, los eventos se acumulan y se procesan cuando vuelva
  • Procesamiento asíncrono: El productor no espera a que los consumidores terminen
  • Auditoría natural: Los eventos forman un log de todo lo que ocurrió en el sistema
  • Extensibilidad: Nuevas funcionalidades se agregan suscribiéndose a eventos existentes

Trade-offs / Desventajas

  • Consistencia eventual: Los datos no se actualizan instantáneamente en todos los servicios
  • Complejidad de debugging: Rastrear el flujo de un evento a través de múltiples servicios es difícil
  • Ordenamiento: Garantizar el orden de procesamiento de eventos requiere diseño cuidadoso
  • Duplicados: Los consumidores deben ser idempotentes para manejar eventos duplicados
  • Infraestructura adicional: Necesitas un broker de mensajería (Kafka, RabbitMQ) con su operación
  • Testing complejo: Testear flujos asíncronos es más difícil que testear llamadas síncronas

Cuándo usar

  • Sistemas donde múltiples servicios necesitan reaccionar al mismo evento
  • Cuando el procesamiento puede ser asíncrono (no necesitas respuesta inmediata)
  • Sistemas con requisitos de alta escalabilidad y throughput
  • Cuando necesitas desacoplar servicios para que evolucionen independientemente
  • Flujos de negocio complejos con múltiples pasos (pedidos, pagos, envíos)
  • Cuando necesitas un log de auditoría de todo lo que ocurre en el sistema

Cuándo evitar

  • Operaciones que requieren respuesta síncrona inmediata (consultas de usuario)
  • Sistemas simples con pocos servicios y flujos lineales
  • Cuando la consistencia fuerte es un requisito no negociable
  • Equipos sin experiencia en sistemas distribuidos y debugging asíncrono
  • Cuando no puedes justificar la infraestructura de un broker de mensajería

Tecnologías e implementaciones comunes

CategoríaOpciones
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
ObservabilidadOpenTelemetry, Jaeger, Zipkin (tracing distribuido)

Relación con otros patrones

  • Saga Pattern: Usa eventos para coordinar transacciones distribuidas entre servicios
  • Outbox Pattern: Garantiza la publicación confiable de eventos junto con cambios en la base de datos
  • CQRS: Separa lecturas y escrituras usando eventos para sincronizar los modelos
  • Idempotencia: Los consumidores deben ser idempotentes para manejar eventos duplicados
  • Circuit Breaker: Protege a los consumidores de sobrecarga cuando hay picos de eventos

Próximos pasos

La arquitectura orientada a eventos es la base de la comunicación en microservicios modernos. Para proteger la integración con sistemas externos que no hablan tu lenguaje de eventos, explora el Anti-Corruption Layer. Para garantizar que los consumidores manejen duplicados correctamente, revisa el patrón de Idempotencia.