saga-patterns
Distributed transaction patterns using orchestration and choreography
What this skill does
# Saga Patterns Skill
## When to Use This Skill
Use this skill when:
- **Saga Patterns tasks** - Working on distributed transaction patterns using orchestration and choreography
- **Planning or design** - Need guidance on Saga Patterns approaches
- **Best practices** - Want to follow established patterns and standards
## Overview
Design distributed transaction patterns using orchestration and choreography for microservices.
## MANDATORY: Documentation-First Approach
Before designing sagas:
1. **Invoke `docs-management` skill** for saga patterns
2. **Verify patterns** via MCP servers (perplexity, context7)
3. **Base guidance on established microservices patterns**
## Saga Fundamentals
```text
Why Sagas?
PROBLEM:
Distributed transactions across services are complex.
Traditional 2PC (Two-Phase Commit) doesn't scale.
SOLUTION:
Saga = Sequence of local transactions
Each step has a compensating action
Eventual consistency instead of ACID
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Step 1 │───►│ Step 2 │───►│ Step 3 │
│ Tx + Cx │ │ Tx + Cx │ │ Tx + Cx │
└─────────┘ └─────────┘ └─────────┘
│ │ │
▼ ▼ ▼
Local Local Local
Transaction Transaction Transaction
Tx = Forward Transaction
Cx = Compensating Transaction
```
## Saga Coordination Styles
### Choreography (Event-Driven)
```text
Choreography Pattern:
Services communicate through events.
No central coordinator.
Each service knows what to do next.
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Order │ │ Payment │ │ Inventory │
│ Service │ │ Service │ │ Service │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
│ OrderCreated │ │
│─────────────────►│ │
│ │ PaymentProcessed │
│ │─────────────────►│
│ │ │ InventoryReserved
│◄─────────────────┼──────────────────│
│ OrderConfirmed │ │
Characteristics:
✓ Loose coupling
✓ Simple services
✗ Hard to track
✗ Cyclic dependencies risk
```
### Orchestration (Coordinator-Driven)
```text
Orchestration Pattern:
Central orchestrator coordinates the saga.
Services expose commands.
Orchestrator manages state.
┌─────────────────┐
│ Orchestrator │
│ (Saga Manager) │
└────────┬────────┘
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Order │ │ Payment │ │ Inventory │
│ Service │ │ Service │ │ Service │
└─────────────┘ └─────────────┘ └─────────────┘
Characteristics:
✓ Clear flow visibility
✓ Easier debugging
✗ Single point of failure
✗ Coupling to orchestrator
```
## Choreography Implementation
### Event-Driven Flow
```csharp
// Order Service - Starts Saga
public class OrderService
{
private readonly IEventPublisher _events;
public async Task CreateOrderAsync(CreateOrderCommand cmd)
{
var order = new Order(cmd.CustomerId, cmd.Items);
await _repository.SaveAsync(order);
// Publish event to start saga
await _events.PublishAsync(new OrderCreated
{
OrderId = order.Id,
CustomerId = cmd.CustomerId,
TotalAmount = order.TotalAmount
});
}
// Handle compensation
public async Task HandleAsync(PaymentFailed @event)
{
var order = await _repository.GetAsync(@event.OrderId);
order.Cancel("Payment failed");
await _repository.SaveAsync(order);
await _events.PublishAsync(new OrderCancelled
{
OrderId = @event.OrderId,
Reason = "Payment failed"
});
}
}
// Payment Service - Reacts to OrderCreated
public class PaymentService
{
public async Task HandleAsync(OrderCreated @event)
{
try
{
var payment = await ProcessPaymentAsync(@event.OrderId, @event.TotalAmount);
await _events.PublishAsync(new PaymentProcessed
{
OrderId = @event.OrderId,
PaymentId = payment.Id
});
}
catch (PaymentException ex)
{
await _events.PublishAsync(new PaymentFailed
{
OrderId = @event.OrderId,
Reason = ex.Message
});
}
}
}
// Inventory Service - Reacts to PaymentProcessed
public class InventoryService
{
public async Task HandleAsync(PaymentProcessed @event)
{
try
{
await ReserveInventoryAsync(@event.OrderId);
await _events.PublishAsync(new InventoryReserved
{
OrderId = @event.OrderId
});
}
catch (InsufficientInventoryException)
{
// Trigger compensation
await _events.PublishAsync(new InventoryReservationFailed
{
OrderId = @event.OrderId
});
}
}
// Compensating action
public async Task HandleAsync(OrderCancelled @event)
{
await ReleaseInventoryAsync(@event.OrderId);
}
}
```
## Orchestration Implementation
### Saga Orchestrator
```csharp
// Saga State Machine
public class OrderSaga : Saga<OrderSagaData>,
IAmStartedBy<OrderCreated>,
IHandle<PaymentProcessed>,
IHandle<PaymentFailed>,
IHandle<InventoryReserved>,
IHandle<InventoryReservationFailed>
{
protected override void ConfigureHowToFindSaga(SagaPropertyMapper<OrderSagaData> mapper)
{
mapper.MapSaga(s => s.OrderId)
.ToMessage<OrderCreated>(m => m.OrderId)
.ToMessage<PaymentProcessed>(m => m.OrderId)
.ToMessage<PaymentFailed>(m => m.OrderId)
.ToMessage<InventoryReserved>(m => m.OrderId)
.ToMessage<InventoryReservationFailed>(m => m.OrderId);
}
public async Task Handle(OrderCreated message, IMessageHandlerContext context)
{
Data.OrderId = message.OrderId;
Data.CustomerId = message.CustomerId;
Data.TotalAmount = message.TotalAmount;
Data.Status = SagaStatus.Started;
// Request payment
await context.Send(new ProcessPaymentCommand
{
OrderId = message.OrderId,
Amount = message.TotalAmount
});
}
public async Task Handle(PaymentProcessed message, IMessageHandlerContext context)
{
Data.PaymentId = message.PaymentId;
Data.Status = SagaStatus.PaymentCompleted;
// Request inventory reservation
await context.Send(new ReserveInventoryCommand
{
OrderId = message.OrderId
});
}
public async Task Handle(PaymentFailed message, IMessageHandlerContext context)
{
Data.Status = SagaStatus.Failed;
// Compensate: Cancel order
await context.Send(new CancelOrderCommand
{
OrderId = message.OrderId,
Reason = "Payment failed"
});
MarkAsComplete();
}
public async Task Handle(InventoryReserved message, IMessageHandlerContext context)
{
Data.Status = SagaStatus.Completed;
// Complete the saga
await context.Publish(new OrderCompleted
{
OrderId = Data.OrderId
});
MarkAsComplete();
}
public async Task Handle(InventoryReservationFailed message, IMessageHandlerContext context)
{
Data.Status = SagaStatus.Failed;
// Compensate: Refund payment
await context.Send(new RefundPaymentCommand
{
OrderId = Data.OrderId,
PaymentId = Data.PaymentId
});
// CompensRelated in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.