← dev-notes
SAGA PATTERN · PARTE 1 DE 2

Saga
Coreografiada

Eventos entre servicios sin coordinador central — cada servicio reacciona y emite

SAGA · P1
📡 Domain Events 🔄 Pasos locales ↩ Compensaciones 🔀 Sin 2PC
01 ¿Qué es una Saga Coreografiada?
🎼
PRINCIPIO FUNDAMENTAL — SIN COORDINADOR CENTRAL
Cada servicio reacciona a un evento, ejecuta su paso local y emite el siguiente evento. Nadie dirige la orquesta — todos conocen su partitura.
La coreografía distribuye la lógica de negocio entre los propios servicios. No hay un punto único de fallo que conozca el estado global. El flujo emerge de la reacción encadenada de eventos. La consistencia final (eventual consistency) es la garantía — no la transacción atómica.
02 Flujo de eventos — ejemplo: Pedido de e-commerce
Camino feliz (Happy Path) — sin fallos
Order
Service
🛒ORDER CREATEDPaso local: reserva
📡 OrderPlaced
Inventory
Service
📦STOCK RESERVEDPaso local: descuenta
📡 StockReserved
Payment
Service
💳PAYMENT PROCESSEDPaso local: cobra
📡 PaymentSucceeded
Shipping
Service
🚚SHIPMENT CREATEDPaso local: envía
📡 Cada evento viaja por un message broker (Kafka, RabbitMQ). Los servicios son productores y consumidores — no se conocen entre sí directamente. El broker garantiza entrega (at-least-once).
03 Compensaciones — qué pasa cuando falla el pago
Camino de compensación — PaymentFailed dispara rollback distribuido
Payment
Service
PAYMENT FAILEDtarjeta rechazada
📡 PaymentFailed
Inventory
Service
STOCK RELEASEDcompensación local
📡 StockReleased
Order
Service
ORDER CANCELLEDcompensación local
⚠️ Idempotencia obligatoria: cada handler de compensación debe ser idempotente — si recibe el mismo evento dos veces (at-least-once delivery), no debe compensar dos veces. Usa IDs de correlación en cada evento.
04 Responsabilidades de cada servicio participante
📡
RESPONSABILIDAD · 01
Publicar eventos de dominio
Domain Event Outbox Pattern at-least-once
Cada servicio debe emitir un evento de dominio cuando completa su paso local con éxito. El evento es la señal para que el siguiente participante actúe. Si el servicio muere antes de emitir, el evento debe poder reenviarse.
Patrón Outbox: persiste el evento en la misma transacción local que el cambio de estado. Un proceso de fondo lee la tabla outbox y lo publica al broker. Garantiza que si el paso local persiste, el evento siempre llega.
👂
RESPONSABILIDAD · 02
Suscribirse y actuar con idempotencia
Consumer Group Idempotency Key Exactly-once semántica
Cada servicio es consumidor de los eventos que le conciernen. El handler debe ser idempotente: procesar el mismo evento N veces debe producir el mismo resultado que procesarlo una sola vez.
Implementación: guarda el event_id en una tabla de eventos procesados. Antes de actuar, verifica si ya lo procesaste — si sí, descarta silenciosamente.
RESPONSABILIDAD · 03
Implementar la transacción compensatoria
Compensating Tx Rollback semántico Estado de negocio
Por cada paso local que puede necesitar deshacerse, debe existir una transacción compensatoria equivalente. No es un rollback técnico — es una operación de negocio que invierte el efecto: liberar stock, devolver pago, cancelar envío.
Límite importante: las compensaciones no pueden "deshacer" acciones ya consumidas por el mundo real (un email enviado, un SMS entregado). Para esos casos, la compensación es una acción de corrección, no un rollback.
🔍
RESPONSABILIDAD · 04
Mantener estado local coherente
State Machine Database per Service Eventual Consistency
El servicio es dueño de su propio estado. Nadie más puede leer ni escribir su base de datos. La máquina de estados local debe contemplar todos los estados posibles: pendiente, procesado, fallido, compensado.
Estados del pedido: PENDING → CONFIRMED → PAID → SHIPPED y los caminos de compensación CANCELLING → CANCELLED
05 Cuándo elegir coreografía — pros y contras
Aspecto
Coreografía ✅
Cuidado ⚠️
Acoplamiento
bajo Los servicios sólo conocen el contrato del evento, no quién lo produce
Cambiar el shape del evento rompe todos los consumidores
Escalabilidad
alta Cada servicio escala independientemente según su carga de eventos
Broker se convierte en cuello de botella si no se diseña bien
Observabilidad
Cada evento es trazable con distributed tracing (trace_id)
difícil El flujo global no es visible en ningún lugar — hay que reconstruirlo
Complejidad lógica
Simple si el flujo es lineal sin muchas ramas
peligrosa Con más de 5-6 participantes el flujo se vuelve un espagueti de eventos
Punto único de fallo
ninguno No hay un coordinador cuya caída paralice el flujo
El broker debe ser HA (High Availability); si cae, el flujo se detiene
Idóneo para
Flujos con pocos pasos, servicios autónomos, arquitectura EDA madura
Evitar en flujos con lógica de negocio compleja, ramificaciones o reintentos elaborados
Señal de que la coreografía es la correcta
Tienes un equipo por servicio, el flujo tiene menos de 5 pasos claros, y ya usas un event broker. Los servicios son autónomos y el acoplamiento temporal no es problema — la velocidad de desarrollo independiente importa más que el control central.
🚨
Señal de que necesitas orquestación en cambio
El flujo tiene más de 6 pasos, con condiciones y ramas (si el pago falla pero el stock se reservó hace 10 min, ¿qué hacemos?). La lógica de compensación es compleja y necesitas un único lugar donde ver el estado global del proceso.