From harness-claude
Implements saga choreography for coordinating distributed workflows via event chains and compensation events without orchestration. Use for linear multi-service transactions like order fulfillment.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Coordinate distributed workflows through event chains and compensation events without an orchestrator.
Designs saga patterns using orchestration and choreography for distributed transactions in microservices. Covers fundamentals, event-driven choreography, central orchestration, and compensating actions.
Implements saga orchestration patterns for coordinating multi-service transactions, handling failures with compensating actions, and managing long-running workflows using Python templates.
Coordinates distributed transactions across microservices using orchestration and choreography sagas with compensating actions for eventual consistency when 2PC fails.
Share bugs, ideas, or general feedback.
Coordinate distributed workflows through event chains and compensation events without an orchestrator.
Order fulfillment saga (choreography):
ORDER CREATED
→ Payment Service: charge card
→ PAYMENT_PROCESSED
→ Inventory Service: reserve stock
→ INVENTORY_RESERVED
→ Shipping Service: create shipment
→ SHIPMENT_CREATED → done
On failure at any step → emit compensation event → previous steps compensate:
PAYMENT_FAILED → saga ends (nothing to compensate)
INVENTORY_FAILED → emit PAYMENT_REFUND_REQUESTED → Payment Service refunds
SHIPPING_FAILED → emit INVENTORY_RELEASE_REQUESTED + PAYMENT_REFUND_REQUESTED
Order service — publishes the saga trigger:
// 1. Create order and publish triggering event
async function createOrder(data: CreateOrderInput): Promise<Order> {
return prisma.$transaction(async (tx) => {
const order = await tx.order.create({
data: { ...data, status: 'AWAITING_PAYMENT' },
});
await tx.outbox.create({
data: {
eventType: 'order.created',
aggregateId: order.id,
payload: {
eventId: crypto.randomUUID(),
orderId: order.id,
userId: order.userId,
amount: order.total,
items: order.items,
},
},
});
return order;
});
}
// Listen for saga completion/failure events
consumer.on('order.saga.completed', async ({ orderId }) => {
await db.order.update({ where: { id: orderId }, data: { status: 'CONFIRMED' } });
});
consumer.on('order.saga.failed', async ({ orderId, reason }) => {
await db.order.update({
where: { id: orderId },
data: { status: 'FAILED', failureReason: reason },
});
});
Payment service — listens and reacts:
consumer.on('order.created', async (event) => {
const { orderId, userId, amount } = event;
try {
const charge = await stripe.charges.create({
amount: Math.round(amount * 100),
currency: 'usd',
customer: await getStripeCustomerId(userId),
});
// Success → publish next saga event
await publish('payment.processed', {
eventId: crypto.randomUUID(),
orderId,
chargeId: charge.id,
amount,
});
} catch (err) {
// Failure → publish compensation event
await publish('payment.failed', {
eventId: crypto.randomUUID(),
orderId,
reason: (err as Error).message,
});
}
});
// Compensation — triggered by downstream failure
consumer.on('payment.refund.requested', async ({ orderId, chargeId }) => {
await stripe.refunds.create({ charge: chargeId });
await publish('payment.refunded', { eventId: crypto.randomUUID(), orderId, chargeId });
});
Inventory service:
consumer.on('payment.processed', async ({ orderId, chargeId, items }) => {
try {
await reserveInventory(items);
await publish('inventory.reserved', { eventId: crypto.randomUUID(), orderId, chargeId });
} catch (err) {
// Cannot reserve → trigger payment compensation
await publish('inventory.reservation.failed', {
eventId: crypto.randomUUID(),
orderId,
chargeId,
reason: (err as Error).message,
});
// Payment service listens to this and refunds
}
});
consumer.on('inventory.release.requested', async ({ orderId, items }) => {
await releaseInventory(items);
await publish('inventory.released', { eventId: crypto.randomUUID(), orderId });
});
Choreography vs. Orchestration:
| Choreography | Orchestration | |
|---|---|---|
| Control | Distributed (each service reacts) | Centralized (saga orchestrator commands) |
| Coupling | Low — services share events | Higher — orchestrator knows all steps |
| Visibility | Hard — no single view of progress | Easy — orchestrator tracks state |
| Complexity | Simple flows | Complex flows, conditional branches |
Idempotency is mandatory: Each saga step must be idempotent — events can be redelivered. Use event IDs and the processed_events table (see events-idempotency skill).
Anti-patterns:
Observability challenge: With pure choreography, no single place shows the full saga state. Solutions:
sagaId across all events)sagaId field for log aggregation queriesmicroservices.io/patterns/data/saga.html