Compensation (Saga Pattern)
Compensation is how Orchestrix rolls back work that already completed when a later part of the flow fails.
This is one of the most important features of the library because real workflows often create side effects in multiple systems. Once one step succeeds and a later one fails, you need a way to restore consistency.
What compensation is for
Use compensation when a step performs a side effect such as:
- reserving inventory
- charging a payment
- creating an external resource
- persisting state that must be undone if the workflow does not finish
Compensation is not about retrying the failed step. It is about undoing work that happened before the failure.
How it works
You can define a compensate function for any step. If the flow fails after that step has completed, Orchestrix automatically runs the compensation.
graph TD
A[Step 1: Reserve Stock] -->|Success| B[Step 2: Charge Card]
B -->|Failure| C[Run Compensation 1: Release Stock]
C --> D[Flow Failed]
B -->|Success| E[Step 3: Send Email]
E -->|Success| F[Flow Completed]Example
flow
.step("reserve-inventory", async (ctx) => {
const id = await inventory.reserve(ctx.input.sku);
ctx.set("reservationId", id);
}, {
compensate: async (ctx) => {
const id = ctx.get<string>("reservationId");
await inventory.release(id);
}
})
.step("process-payment", async (ctx) => {
await payment.charge(ctx.input.amount);
});If process-payment fails, Orchestrix runs the compensation for reserve-inventory.
What compensation does not do
The failed step itself is not compensated automatically just because it failed.
Why? Because compensation is intended for work that already completed successfully. A failed step is not considered committed work.
Compensation order
Compensations run in reverse order of successful completion.
If Step 1 and Step 2 completed and Step 3 failed, Orchestrix runs:
- compensation for Step 2
- compensation for Step 1
This reverse ordering matters because later steps often depend on work created earlier.
Compensation in parallel blocks
When a parallel group fails and some steps in that group completed successfully, Orchestrix may compensate those successful steps as part of failure handling.
This is why parallel steps should also define compensation when they create meaningful side effects.
What happens if compensation fails
Compensation failures do not replace the original flow error.
The flow still reports the original failure as the main outcome. This is intentional: the root cause of the workflow failure should remain visible.
Because of that, compensation functions should be:
- small
- well-tested
- idempotent when possible
Designing good compensation
Good compensation usually:
- undoes one specific side effect
- reads any needed identifiers from context
- is safe to run more than once
- avoids creating new business complexity
Best Practices
- Add compensation to steps that reserve, create, charge, or allocate something.
- Store rollback identifiers in context at the moment they are created.
- Keep compensations focused and explicit.
- Treat compensation as part of the business design, not an afterthought.