Documentation
¶
Overview ¶
Command order-pipeline is a realistic order-fulfilment **saga**: the workflow commits a sequence of steps, and if a later step fails it runs a compensation for each committed step, in reverse, to leave the world consistent.
DATABASE_URL=postgres://localhost:5432/river_dev?sslmode=disable \
go run ./examples/order-pipeline
The forward path and each step's compensation:
- reserve inventory → release inventory
- authorize payment → void the authorization
- capture payment → refund the payment (supersedes the void)
- ship → (final step)
Payment is a two-phase hold-then-settle, so its compensation changes as the saga advances: before capture you *void* the hold; after capture you *refund* the money. Capturing only just before shipping means a failure earlier never takes money we can't fulfil.
The design rule this example follows (see docs/failure-and-cancellation.md): the workflow **never returns an error for a business or step failure**. It compensates and completes with a domain status ("out_of_stock", "declined", "shipping_failed", …). It returns an error only if a *compensation itself* fails — the one situation it can't resolve automatically, where an operator must step in.
An activity signals non-success in one of three ways, all shown below:
- a result field (Reserved=false, Approved=false) — an expected business branch the workflow reads and acts on;
- river.JobCancel — a permanent failure, terminal immediately with no wasted retries (the carrier that can't serve a destination);
- a returned error — a transient failure that retries under the activity's own policy, and only reaches the workflow once terminal.