Que es la Arquitectura Orientada a Eventos
La Arquitectura Orientada a Eventos (EDA: Event-Driven Architecture) es un patron de diseno en el que la comunicacion entre sistemas se realiza a traves de eventos (notificaciones de sucesos). Reduce las dependencias entre componentes y permite construir sistemas escalables y flexibles.
Que es un evento: Es un mensaje que representa un cambio de estado significativo dentro del sistema. Ejemplos de eventos son “Un usuario se ha registrado”, “Un pedido se ha completado”, “El inventario ha disminuido”.
Diferencias con el Enfoque Tradicional Basado en Solicitudes
Orientado a Solicitudes (Sincrono)
flowchart LR
Order["Servicio de Pedidos"] --> Stock["Servicio de Inventario"] --> Payment["Servicio de Pagos"] --> Notify["Servicio de Notificaciones"]
Note["Espera la respuesta de cada servicio"]
- Los servicios estan fuertemente acoplados
- Una falla en un servicio afecta a todo el sistema
- El tiempo de procesamiento es la suma de todos los servicios
Orientado a Eventos (Asincrono)
flowchart TB
Order["Servicio de Pedidos"] --> Event["Evento de Pedido Completado"]
Event --> Stock["Servicio de Inventario<br/>(procesa independientemente)"]
Event --> Payment["Servicio de Pagos<br/>(procesa independientemente)"]
Event --> Notify["Servicio de Notificaciones<br/>(procesa independientemente)"]
- Los servicios estan desacoplados
- Las fallas se localizan
- El procesamiento paralelo es posible
Tipos de Eventos
Eventos de Dominio
Representan sucesos que ocurren en el dominio del negocio.
// Ejemplo de evento de dominio
{
"eventType": "OrderPlaced",
"eventId": "evt_123456",
"timestamp": "2024-01-15T10:30:00Z",
"payload": {
"orderId": "ord_789",
"customerId": "cust_456",
"items": [...],
"totalAmount": 5000
}
}
Eventos de Integracion
Eventos compartidos entre diferentes servicios.
Eventos de Notificacion
Eventos que solo notifican cambios de estado sin incluir datos detallados.
// Evento de notificacion (Fat vs Thin)
// Thin Event - Se requiere obtener detalles por separado
{ "eventType": "UserUpdated", "userId": "123" }
// Fat Event - Incluye toda la informacion necesaria
{ "eventType": "UserUpdated", "userId": "123", "name": "Alice", "email": "..." }
Event Sourcing
Un patron que almacena el estado como un historial de eventos.
CRUD Tradicional
flowchart TB
subgraph CRUD["CRUD Tradicional - Solo almacena el estado actual"]
Orders["orders<br/>id: 1, status: 'shipped'"]
end
Event Sourcing
flowchart TB
subgraph Events["Almacena todos los eventos"]
E1["1. OrderCreated (2024-01-01)"]
E2["2. PaymentReceived (2024-01-02)"]
E3["3. OrderShipped (2024-01-03)"]
end
Events -->|Reproducir| State["Estado actual: status = 'shipped'"]
Ventajas
- Registro de auditoria completo: Se puede rastrear todo el historial de cambios
- Viaje en el tiempo: Se puede reconstruir el estado en cualquier punto
- Reproduccion de eventos: Se puede reconstruir el estado despues de corregir errores
Desventajas
- Aumenta la complejidad
- La evolucion del esquema de eventos es un desafio
- Se necesitan estrategias para el rendimiento de lectura
CQRS (Separacion de Responsabilidades de Comando y Consulta)
Un patron que separa los modelos de lectura (Query) y escritura (Command).
flowchart TB
Command["Command<br/>(Modelo de escritura)"]
Command -->|Emitir evento| EventStore["Event Store"]
EventStore -->|Proyeccion| Query["Query<br/>(Modelo de lectura)"]
Modelo de Escritura
// Manejador de comandos
async function handlePlaceOrder(command) {
const order = new Order(command.orderId);
order.addItems(command.items);
order.place();
await eventStore.save(order.getUncommittedEvents());
}
Modelo de Lectura
// Vista optimizada para lectura
const orderSummary = {
orderId: "123",
customerName: "Alice", // Informacion del cliente ya unida
itemCount: 3,
totalAmount: 5000,
status: "shipped"
};
Patrones de Implementacion
Patron Saga
Implementa transacciones que abarcan multiples servicios mediante una cadena de eventos.
1. Crear pedido → OrderCreated
2. Reservar inventario → InventoryReserved (o InventoryReservationFailed en caso de fallo)
3. Procesar pago → PaymentProcessed (o PaymentFailed en caso de fallo)
4. Confirmar pedido → OrderConfirmed
En caso de fallo, transaccion de compensacion:
PaymentFailed → Liberar inventario (compensacion) → Cancelar pedido
Patron Outbox
Un patron para garantizar la actualizacion de la base de datos y la emision de eventos.
1. Dentro de la transaccion:
- Actualizar datos del negocio
- Insertar evento en la tabla outbox
2. En un proceso separado:
- Sondear la tabla outbox
- Emitir eventos a la cola de mensajes
- Marcar como emitido
Consideraciones a Tener en Cuenta
Consistencia Eventual
Servicio de pedidos: Estado del pedido = "completado"
Servicio de inventario: Evento aun no procesado → Existe periodo de inconsistencia
↓ Despues de procesar el evento
Servicio de inventario: Inventario reducido → Consistencia recuperada
Orden de Eventos
Orden correcto:
1. OrderCreated
2. OrderUpdated
3. OrderShipped
Si el orden se altera:
1. OrderShipped (?)
2. OrderCreated
→ El estado puede corromperse
Idempotencia
Disenar para que el resultado no cambie aunque el mismo evento se entregue multiples veces.
async function handleInventoryReserved(event) {
// Verificar duplicados con clave de idempotencia
const processed = await db.processedEvents.findById(event.eventId);
if (processed) return;
await db.inventory.reserve(event.payload);
await db.processedEvents.insert({ eventId: event.eventId });
}
Resumen
La arquitectura orientada a eventos es un patron poderoso para disenar sistemas complejos de manera desacoplada y flexible. Al combinarla con Event Sourcing y CQRS, se puede mejorar la auditabilidad y la escalabilidad. Sin embargo, es necesario abordar desafios especificos de sistemas distribuidos como la consistencia eventual y el orden de eventos.
← Volver a la lista