Nivel 4 · 35 min
Patrón Saga
El patrón Saga gestiona transacciones distribuidas en sistemas de microservicios donde una transacción ACID distribuida no es posible. Una saga es una secuencia de transacciones locales donde cada transacción publica eventos o mensajes que disparan el siguiente paso.
Coreografía vs Orquestación
En Coreografía, no hay coordinador central —cada servicio reacciona a eventos y publica eventos para disparar el siguiente paso. Altamente desacoplado pero difícil de rastrear el flujo completo. En Orquestación, hay un Saga Orchestrator central que envía commands a cada servicio y espera respuestas. Más fácil de rastrear, pero el orquestador es un punto central de coordinación.
Transacciones Compensatorias
Cuando un paso falla después de que pasos anteriores ya committearon, la saga ejecuta transacciones compensatorias —operaciones semánticas de deshacer. ReserveInventory → compensación: ReleaseInventory. ChargePayment → compensación: RefundPayment. Las compensaciones no son ACID rollback —algunos efectos no se pueden deshacer (email enviado, SMS recibido).
Manejo de Fallos
Los fallos en una saga son complejos: ¿qué pasa si la compensación también falla? Estrategias: retry con backoff exponencial para fallos transitorios, dead letter queue para fallos persistentes, estado de saga en tabla dedicada para reintentar manualmente, idempotencia en todos los pasos (mismo command dos veces = mismo resultado).
Code example
// Saga con orquestador
class PlaceOrderSaga {
enum State { STARTED, INVENTORY_RESERVED, PAYMENT_CHARGED,
INVENTORY_RELEASED, PAYMENT_REFUNDED, COMPLETED, FAILED }
void start(PlaceOrderCommand cmd) {
inventoryService.reserveItems(cmd.items());
}
@EventHandler
void on(InventoryReserved event) {
state = INVENTORY_RESERVED;
paymentService.charge(event.orderId(), orderTotal);
}
@EventHandler
void on(PaymentFailed event) {
// Compensación: liberar el inventario reservado
inventoryService.releaseItems(event.orderId());
state = INVENTORY_RELEASED;
}
@EventHandler
void on(InventoryReleased event) {
state = FAILED;
}
}