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
| Concepto | Descripción | Ejemplo |
|---|---|---|
| Evento | Registro inmutable de algo que ocurrió | OrderCreated, PaymentProcessed |
| Productor | Servicio que publica eventos | Order Service publica OrderCreated |
| Consumidor | Servicio que reacciona a eventos | Inventory Service escucha OrderCreated |
| Event Bus/Broker | Infraestructura que transporta eventos | Kafka, RabbitMQ, Amazon SNS/SQS |
| Topic/Queue | Canal por donde fluyen los eventos | orders.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ía | Descripción | Uso |
|---|---|---|
| At-most-once | El evento se entrega como máximo una vez (puede perderse) | Métricas, analytics |
| At-least-once | El evento se entrega al menos una vez (puede duplicarse) | La mayoría de casos (con idempotencia) |
| Exactly-once | El evento se entrega exactamente una vez | Transacciones 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ía | Opciones |
|---|---|
| Event Streaming | Apache Kafka, Amazon Kinesis, Azure Event Hubs |
| Message Brokers | RabbitMQ, Amazon SQS/SNS, Google Pub/Sub, NATS |
| Event Store | EventStoreDB, Kafka (como event store) |
| Frameworks | Spring Cloud Stream, MassTransit (.NET), Axon Framework |
| Observabilidad | OpenTelemetry, 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.