> Coordinate distributed transactions using choreography and orchestration sagas with compensation.
Scanned 9/11/2026
Install to Claude Code
npx -y skills add Intense-Visions/harness-engineering --skill microservices-saga-pattern --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Microservices Saga Pattern?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/intense-visions-microservices-saga-pattern)More formats (shields.io, HTML) on the badges page.
# Microservices: Saga Pattern
> Coordinate distributed transactions using choreography and orchestration sagas with compensation.
## When to Use
- A business transaction spans multiple services and you can't use a distributed 2-phase commit
- You need eventual consistency with rollback capability when any step fails
- You're processing orders, bookings, financial transfers, or any multi-step workflow
- You need visibility into the overall progress of a multi-step distributed operation
## Instructions
**Orchestration saga (central coordinator — use for complex flows):**
```typescript
// The saga orchestrator is a state machine that commands each service
type SagaState =
| { step: 'pending' }
| { step: 'payment_pending'; orderId: string }
| { step: 'payment_complete'; orderId: string; chargeId: string }
| { step: 'inventory_pending'; orderId: string; chargeId: string }
| { step: 'inventory_reserved'; orderId: string; chargeId: string; reservationId: string }
| { step: 'completed'; orderId: string }
| { step: 'compensating'; failedAt: string; reason: string }
| { step: 'failed'; reason: string };
class OrderSagaOrchestrator {
constructor(
private readonly paymentService: PaymentServiceClient,
private readonly inventoryService: InventoryServiceClient,
private readonly shippingService: ShippingServiceClient,
private readonly db: SagaRepository
) {}
async execute(sagaId: string, input: OrderSagaInput): Promise<void> {
await this.db.updateState(sagaId, { step: 'payment_pending', orderId: input.orderId });
// Step 1: Process payment
let chargeId: string;
try {
const result = await this.paymentService.charge({
orderId: input.orderId,
userId: input.userId,
amount: input.amount,
});
chargeId = result.chargeId;
} catch (err) {
await this.db.updateState(sagaId, { step: 'failed', reason: 'Payment failed' });
return;
}
await this.db.updateState(sagaId, {
step: 'payment_complete',
orderId: input.orderId,
chargeId,
});
// Step 2: Reserve inventory
let reservationId: string;
try {
const result = await this.inventoryService.reserve({
orderId: input.orderId,
items: input.items,
});
reservationId = result.reservationId;
} catch (err) {
// Compensate: refund payment
await this.compensate(sagaId, chargeId, null, 'Inventory unavailable');
return;
}
await this.db.updateState(sagaId, {
step: 'inventory_reserved',
orderId: input.orderId,
chargeId,
reservationId,
});
// Step 3: Create shipment
try {
await this.shippingService.createShipment({ orderId: input.orderId, address: input.address });
} catch (err) {
// Compensate: release inventory AND refund payment
await this.compensate(sagaId, chargeId, reservationId, 'Shipping failed');
return;
}
await this.db.updateState(sagaId, { step: 'completed', orderId: input.orderId });
}
private async compensate(
sagaId: string,
chargeId: string,
reservationId: string | null,
reason: string
): Promise<void> {
await this.db.updateState(sagaId, { step: 'compensating', failedAt: 'shipping', reason });
// Compensate in reverse order
if (reservationId) {
await this.inventoryService
.releaseReservation(reservationId)
.catch((e) => console.error('Compensation failed: release inventory', e));
}
await this.paymentService
.refund(chargeId)
.catch((e) => console.error('Compensation failed: refund', e));
await this.db.updateState(sagaId, { step: 'failed', reason });
}
}
// Saga table
/*
CREATE TABLE sagas (
id UUID PRIMARY KEY,
type TEXT NOT NULL,
state JSONB NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
completed_at TIMESTAMPTZ
);
*/
```
**Saga step with idempotency (compensatable operations must be idempotent):**
```typescript
class PaymentServiceClient {
async charge(input: ChargeInput): Promise<ChargeResult> {
const idempotencyKey = `saga:${input.orderId}:charge`;
const response = await fetch(`${this.baseUrl}/charges`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Idempotency-Key': idempotencyKey,
},
body: JSON.stringify(input),
});
if (!response.ok) throw new Error(`Charge failed: HTTP ${response.status}`);
return response.json();
}
async refund(chargeId: string): Promise<void> {
const idempotencyKey = `refund:${chargeId}`;
const response = await fetch(`${this.baseUrl}/charges/${chargeId}/refund`, {
method: 'POST',
headers: { 'Idempotency-Key': idempotencyKey },
});
if (!response.ok) throw new Error(`Refund failed: HTTP ${response.status}`);
}
}
```
## Details
**Choreography vs. Orchestration (recap):**
- **Choreography:** Services react to events. No central coordinator. Good for simple flows.
- **Orchestration:** A saga object commands each service. Central visibility. Better for complex flows with branching.
**Pivot transaction:** The point of no return in a saga. Before the pivot, all steps can be compensated. After the pivot, steps must complete (they don't roll back — they may need "forward recovery").
**Anti-patterns:**
- Compensation that can also fail — use retries with backoff; log and alert on persistent compensation failures
- Not persisting saga state — if the orchestrator crashes mid-saga, you have no way to resume
- Synchronous saga steps that all use 2PC under the hood — defeats the purpose; use async or accept eventual consistency
**Saga vs. Two-Phase Commit:** 2PC is synchronous and requires all participants to be available simultaneously. Saga is asynchronous and tolerant of temporary failures. 2PC guarantees strong consistency; Saga guarantees eventual consistency with compensating transactions.
**Monitoring:** Expose saga state via a monitoring dashboard or query endpoint. Alert on sagas stuck in `compensating` or `pending` states beyond a threshold.
## Source
microservices.io/patterns/data/saga.html
## Process
1. Read the instructions and examples in this document.
2. Apply the patterns to your implementation, adapting to your specific context.
3. Verify your implementation against the details and edge cases listed above.
## Harness Integration
- **Type:** knowledge — this skill is a reference document, not a procedural workflow.
- **No tools or state** — consumed as context by other skills and agents.
## Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!