Skip to content

Saga Pattern Guide

The Saga Pattern is a way to keep multi-step workflows consistent when they span several operations that cannot be wrapped in one database transaction.

Orchestrix supports this pattern through step-level compensation.

The problem

In a monolith with one transactional database, you may be able to commit or roll back everything together.

In real application workflows, that is often not possible. You may:

  • reserve inventory in one system
  • create a record in another
  • charge money through an external provider

If a later action fails, the earlier actions may need to be undone.

The Orchestrix approach

In Orchestrix, each step can define a compensate function.

That gives you:

  • forward execution through normal steps
  • backward recovery through compensation if the flow fails

Example: booking a trip

ts
const tripBookingFlow = create("book-trip")
  .step("book-flight", async (ctx) => {
    const flightId = await flightService.book(ctx.input.flightDetails);
    ctx.set("flightId", flightId);
  }, {
    compensate: async (ctx) => {
      await flightService.cancel(ctx.get("flightId"));
    }
  })
  .step("book-hotel", async (ctx) => {
    const hotelId = await hotelService.book(ctx.input.hotelDetails);
    ctx.set("hotelId", hotelId);
  }, {
    compensate: async (ctx) => {
      await hotelService.cancel(ctx.get("hotelId"));
    }
  })
  .step("charge-customer", async (ctx) => {
    await paymentService.charge(ctx.input.userId, ctx.input.totalAmount);
  });

Failure scenario

If charge-customer fails:

  1. book-hotel compensation runs
  2. book-flight compensation runs
  3. the flow returns a failed result

This reverse order is what makes rollback coherent.

Forward recovery and backward recovery

There are two complementary ideas here:

  • forward recovery: retry and try to finish successfully
  • backward recovery: compensate previously completed work

Orchestrix supports both:

  • retries at the step level
  • compensation for completed steps when the flow fails

In production workflows, you often use both together.

What makes a good saga step

A good saga step:

  • produces a meaningful business action
  • stores the identifier needed to undo that action
  • defines a compensation function if the action is not safe to leave behind

Examples of useful context values:

  • reservation IDs
  • payment IDs
  • created resource IDs

Eventual consistency

Saga-based workflows give you eventual consistency, not one giant atomic transaction.

That means intermediate states can exist briefly while the workflow is in progress. Your system design should tolerate those states.

Best Practices

  • Add compensation to every durable side effect that matters to business consistency.
  • Store undo identifiers in context as soon as they are created.
  • Make compensation functions idempotent whenever possible.
  • Use hooks to observe when compensation runs, because compensation usually signals an important operational event.

Released under the MIT License.