Saga Step
A saga step represents one action in a Saga—a distributed
transaction that coordinates changes across multiple components. Each step
defines both a forward action (do) and a compensating action (undo) to
enable rollback if later steps fail.
Purpose¶
Saga steps provide:
- Atomic operations: Each step succeeds or fails as a unit
- Compensation logic: Define how to reverse an action if needed
- Clear sequencing: Steps execute in defined order
- Distributed coordination: Manage state across multiple entities
Syntax¶
saga ProcessPayment is {
step ReserveInventory is {
input: { orderId: OrderId, items: OrderItem* }
output: { reservationId: ReservationId }
do {
tell entity Inventory to ReserveItems
}
undo {
tell entity Inventory to ReleaseReservation
}
}
step ChargePayment is {
input: { customerId: CustomerId, amount: Money }
output: { transactionId: TransactionId }
do {
tell entity PaymentService to ProcessCharge
}
undo {
tell entity PaymentService to RefundCharge
}
}
step CreateOrder is {
input: { customerId: CustomerId, items: OrderItem* }
output: { orderId: OrderId }
do {
tell entity OrderService to CreateOrder
}
undo {
tell entity OrderService to CancelOrder
}
}
}
Step Components¶
| Component | Required | Description |
|---|---|---|
input |
Yes | Data the step receives |
output |
Yes | Data the step produces |
do |
Yes | Forward action to perform |
undo |
Yes | Compensation action for rollback |
Execution Flow¶
When a saga executes:
- Steps run in sequence, each
doaction executing in order - If a step fails, the saga reverses direction
- Each completed step's
undoaction runs in reverse order - The saga completes when all compensations finish
Normal flow: Step1.do → Step2.do → Step3.do → Success
Failure at S3: Step1.do → Step2.do → Step3.do (fails)
↓
Compensation: Step2.undo → Step1.undo → Saga failed (clean state)
Example: Order Fulfillment Saga¶
saga FulfillOrder is {
|Coordinates order fulfillment across inventory,
|payment, and shipping services.
step ValidateOrder is {
input: { orderId: OrderId }
output: { validated: Boolean }
do { tell entity OrderValidator to ValidateOrder }
undo { prompt "No compensation needed for validation" }
}
step ReserveStock is {
input: { orderId: OrderId, items: OrderItem* }
output: { reservationId: ReservationId }
do { tell entity Inventory to ReserveItems }
undo { tell entity Inventory to ReleaseItems }
}
step ProcessPayment is {
input: { orderId: OrderId, amount: Money }
output: { paymentId: PaymentId }
do { tell entity PaymentGateway to Charge }
undo { tell entity PaymentGateway to Refund }
}
step ShipOrder is {
input: { orderId: OrderId, address: Address }
output: { trackingNumber: String }
do { tell entity ShippingService to CreateShipment }
undo { tell entity ShippingService to CancelShipment }
}
}
Best Practices¶
- Make steps idempotent: Steps should be safe to retry
- Keep steps small: Each step should do one thing
- Design compensations carefully: Ensure undo truly reverses the action
- Handle partial failures: Some actions can't be fully undone (e.g., sent emails)—design accordingly
- Log everything: Saga debugging requires visibility into each step
Occurs In¶
Contains¶
- Statements within
doandundoblocks