Every stage declares its compensator
public interface CompensatingStage extends PipelineStage {
void compensate(PipelineContext ctx);
}
public class CreateOrderStage implements CompensatingStage {
public void run(PipelineContext ctx) {
long orderId = orderService.create(ctx.get("order"));
ctx.put("orderId", orderId);
}
public void compensate(PipelineContext ctx) {
ctx.get("orderId", Long.class)
.ifPresent(orderService::cancel);
}
} Advertisement
The saga runner
Runs stages forward, tracks which succeeded. On failure, runs compensators in reverse order.
Advertisement
Idempotent compensators
Compensators MUST be idempotent — they might be retried if the compensation itself fails. Cancelling an already-cancelled order should be a no-op, not an error.