Hacer un pedido toca tres servicios: inventario reserva el stock, pagos autoriza la tarjeta y envíos crea un envío. En un monolito eso es una sola transacción de base de datos. Dividido en servicios con sus propias bases de datos, no existe una transacción que abarque los tres, y el two-phase commit entre servicios rara vez es una opción en la práctica.
El patrón saga es la respuesta estándar. Este artículo explica cómo funciona una saga, la diferencia entre orquestación y coreografía, cómo encajan las compensaciones y los pasos pivote, y los problemas de aislamiento que las sagas no te resuelven. El diagrama muestra una saga de pedido orquestada.
Qué es una saga
Una saga es una secuencia de transacciones locales. Cada paso hace commit en la base de datos de su propio servicio y dispara el siguiente. Si un paso falla, la saga ejecuta transacciones de compensación que deshacen los efectos de los pasos que ya hicieron commit.
La idea es anterior a los microservicios: Hector Garcia-Molina y Kenneth Salem describieron las sagas en un artículo de 1987 sobre transacciones de base de datos de larga duración. El patrón volvió porque las fronteras entre servicios recrearon el mismo problema.
Dos consecuencias lo condicionan todo:
- Consistencia eventual. Entre el primer y el último paso, el sistema está en un estado intermedio que otras peticiones pueden ver. El pedido está en PENDING, el stock está reservado y la tarjeta está autorizada pero sin cobrar.
- Compensar no es hacer rollback. No puedes «desenviar» un correo ni devolver un cargo en silencio. Una compensación es una nueva acción de negocio, como anular una autorización o emitir un reembolso, que revierte semánticamente la anterior.
Pasos compensables, pivote y reintentables
Clasifica cada paso antes de dibujar nada. La guía del patrón saga del Azure Architecture Center usa tres tipos:
| Tipo | Significado | Ejemplo |
|---|---|---|
| Compensable | Se puede deshacer con otra transacción | Reservar stock; autorizar el pago |
| Pivote | El punto de no retorno: cuando tiene éxito, la saga debe terminar | Crear el envío |
| Reintentable | Va después del pivote y tiene que acabar teniendo éxito; idempotente | Enviar el correo de confirmación |
El orden importa. Pon primero los pasos compensables, luego el pivote y después solo pasos reintentables. Si el pivote falla, compensa; una vez que tiene éxito, reintenta todo lo que viene después hasta que salga bien. Una saga con un paso compensable después del pivote no puede llegar a un estado consistente cuando ese paso falla.
Orquestación vs. coreografía
Hay dos formas de coordinar los pasos.
Orquestación: un orquestador de sagas, a menudo una máquina de estados con estado persistente, le dice a cada servicio qué hacer y espera la respuesta. Se encarga de la secuencia, los reintentos, los timeouts y las compensaciones.
Coreografía: no hay coordinador. Cada servicio escucha eventos y reacciona: inventario recibe OrderCreated y reserva stock, pagos recibe StockReserved y autoriza, y así sucesivamente. Los fallos también son eventos, y cada servicio sabe qué compensación ejecutar.
| Orquestación | Coreografía | |
|---|---|---|
| Dónde vive el flujo | En un solo lugar: el orquestador | Repartido entre los manejadores de eventos de los servicios |
| Añadir un paso | Cambiar el orquestador | Cambiar quién escucha qué evento |
| Ver el estado de un pedido | Consultar el log de la saga | Reconstruirlo a partir de los eventos |
| Acoplamiento | Los servicios conocen los comandos del orquestador | Los servicios conocen los eventos de los demás |
| Encaja bien con | Muchos pasos, ramificaciones, orden estricto | Dos o tres pasos, flujos simples |
La coreografía es cómoda en flujos cortos y dolorosa cuando alguien pregunta «¿por qué está atascado el pedido 8812?». La orquestación le da a esa pregunta un único lugar donde mirar. Motores de workflow como Temporal, AWS Step Functions o Camunda ofrecen orquestación persistente para que no tengas que construir la máquina de estados tú mismo. La página de Chris Richardson sobre el patrón saga tiene ejemplos desarrollados de ambos estilos.
Aislamiento: la parte que las sagas te dejan a ti
Una transacción de base de datos oculta el estado intermedio a otras transacciones. Una saga no. Mientras se ejecuta una saga de pedido, otra petición puede leer el stock reservado, o una segunda saga puede modificar el mismo registro. Las contramedidas habituales:
- Bloqueo semántico. Marca los registros como en curso (un pedido en PENDING, stock en RESERVED) para que otras operaciones sepan que deben esperar o rechazar.
- Actualizaciones conmutativas. Diseña las operaciones para que den el mismo resultado en cualquier orden, por ejemplo incrementando y decrementando contadores en lugar de fijarlos.
- Releer antes de escribir. Comprueba que un valor no ha cambiado desde que la saga lo leyó y, si cambió, falla o reinicia.
Haz idempotentes todos los pasos y todas las compensaciones, con el ID de la saga y el paso como clave. Los mensajes se entregarán dos veces, y un «anular autorización» reintentado no debe anular otro pago.
Timeouts y sagas atascadas
Cada paso necesita un plazo. Un servicio de pagos que nunca responde deja la saga esperando para siempre y el stock reservado. Ponle un timeout a cada paso, trata un timeout como un fallo antes del pivote (compensar) y como motivo para reintentar después, y crea alertas para las sagas que permanecen en un estado más tiempo del esperado. Un pequeño panel de sagas por paso actual responde por sí solo a la mayoría de las preguntas de soporte. Registra también por qué terminó cada saga.
El diagrama de arquitectura
Contenido del diagrama
- Order service
- Saga orchestrator
- Saga log
- Inventory service
- Payment service
- Shipping service
- All steps succeeded?
- Order APPROVED
- Compensate in reverse order
- Order REJECTED
- start
- persist each step
- 1. reserve stock
- 2. authorize payment
- 3. create shipment
- yes
- no
- void, release
Cómo leerlo:
- Izquierda: el servicio de pedidos crea el pedido en PENDING e inicia la saga.
- Centro: el orquestador ejecuta los pasos en orden y persiste cada uno en el log de la saga, para que un reinicio continúe donde se quedó.
- Derecha: los tres servicios, cada uno etiquetado con su número de paso y su tipo. Inventario y pagos son compensables; envíos es el pivote.
- Abajo: si todos los pasos tienen éxito, el pedido pasa a APPROVED. Si falla un paso anterior al pivote, el orquestador compensa en orden inverso (anula la autorización y luego libera el stock) y el pedido pasa a REJECTED.
- La tabla Markdown empareja cada acción con su compensación, que es lo primero que debería comprobar quien revise.
Qué poner en un diagrama de saga
- El orden de los pasos y el tipo de cada uno. Compensable, pivote o reintentable va en la caja.
- Cada compensación. Un paso sin compensación es el pivote, es reintentable o es un bug.
- Dónde vive el estado de la saga. Una saga que no sobrevive a una caída a mitad de camino no está terminada.
- Los estados intermedios visibles. PENDING y RESERVED forman parte del diseño; otras funcionalidades los verán.
Las sagas encajan de forma natural con el patrón transactional outbox, porque cada paso debe actualizar su base de datos y enviar un mensaje sin perder ninguna de las dos cosas. El diagrama de arquitectura de microservicios muestra dónde se sitúan estos servicios, y el diagrama de integración de una pasarela de pago trata en detalle los pasos de autorización y captura.
Constrúyelo en Archboard
- Inicia sesión con Google o GitHub y crea un diagrama.
- Añade nodos Contenedor del pack Modelo C4 para el servicio de pedidos, el orquestador de sagas y los tres servicios participantes, y una Base de datos para el log de la saga.
- Pon el número y el tipo de paso (compensable o pivote) en el campo de tecnología de cada servicio.
- Añade una Decisión, un Proceso y dos nodos Inicio / Fin del pack Diagrama de flujo para los caminos de éxito y de compensación.
- Añade un bloque Markdown del pack Básico con una tabla de acciones y compensaciones.
El ejemplo usa 11 nodos de diagrama, dentro del plan Free.
Preguntas frecuentes
¿Qué diferencia hay entre una saga y una transacción distribuida? Una transacción distribuida (two-phase commit) bloquea recursos en varias bases de datos hasta que todos hacen commit. Una saga hace commit de cada paso en local y deshace los pasos anteriores con compensaciones si falla uno posterior: cambia aislamiento por disponibilidad.
¿Debo usar orquestación o coreografía? Orquestación para flujos con muchos pasos, ramificaciones u orden estricto, porque el estado de cada saga vive en un solo lugar. Coreografía para flujos cortos en los que dos o tres servicios reaccionan a los eventos de los demás.
¿Qué es una transacción pivote? El paso a partir del cual la saga ya no se puede compensar. Todo lo anterior debe ser compensable; todo lo posterior, reintentable hasta que tenga éxito.
¿Cómo gestiono los fallos en una compensación? Reinténtala. Las compensaciones deben ser idempotentes y acabar teniendo éxito; si una no puede, la saga necesita una vía de resolución manual y una alerta.
¿Me dan aislamiento las sagas? No. Otras peticiones pueden ver los estados intermedios. Usa bloqueos semánticos como los estados PENDING, actualizaciones conmutativas y relecturas para evitar las anomalías que importan a tu negocio.