3.7 KiB
3.7 KiB
Sagas
Long-running workflows with compensation logic for distributed transactions.
Overview
Sagas coordinate multiple steps in a workflow, with compensation logic to handle failures. They listen to events, execute business logic, and publish new events to continue the workflow.
Key Features:
- ✅ ISaga - Interface for saga implementations
- ✅ Multi-Step Workflows - Coordinate complex processes
- ✅ Compensation - Rollback on failures
- ✅ State Management - Track saga progress
- ✅ Event-Driven - React to domain events
Quick Start
public class OrderFulfillmentSaga : ISaga
{
private readonly IEventStreamStore _eventStore;
private readonly IInventoryService _inventoryService;
private readonly IPaymentService _paymentService;
private readonly IShippingService _shippingService;
public async Task HandleAsync(OrderPlacedEvent @event, CancellationToken ct)
{
try
{
// Step 1: Reserve inventory
await _inventoryService.ReserveAsync(@event.OrderId, @event.Items);
await PublishEventAsync(new InventoryReservedEvent { OrderId = @event.OrderId });
// Step 2: Process payment
await _paymentService.ChargeAsync(@event.OrderId, @event.TotalAmount);
await PublishEventAsync(new PaymentProcessedEvent { OrderId = @event.OrderId });
// Step 3: Ship order
await _shippingService.ShipAsync(@event.OrderId);
await PublishEventAsync(new OrderShippedEvent { OrderId = @event.OrderId });
}
catch (Exception ex)
{
// Compensation: Rollback
await CompensateAsync(@event.OrderId);
await PublishEventAsync(new OrderFailedEvent
{
OrderId = @event.OrderId,
Reason = ex.Message
});
}
}
private async Task CompensateAsync(int orderId)
{
// Release inventory
await _inventoryService.ReleaseAsync(orderId);
// Refund payment
await _paymentService.RefundAsync(orderId);
}
private async Task PublishEventAsync(object @event)
{
await _eventStore.AppendAsync("orders", new[] { @event });
}
}
Saga Pattern
OrderPlacedEvent
↓
Reserve Inventory
↓
InventoryReservedEvent
↓
Process Payment
↓
PaymentProcessedEvent
↓
Ship Order
↓
OrderShippedEvent
(If any step fails → Compensate all previous steps)
Features
Saga Pattern
Understand the saga pattern and when to use it.
Creating Sagas
Implement ISaga for long-running workflows.
Compensation
Handle failures with rollback logic.
Common Use Cases
Order Fulfillment:
Place Order → Reserve Inventory → Charge Payment → Ship Order
(Compensation: Release Inventory → Refund Payment)
User Registration:
Register → Send Verification Email → Wait for Confirmation → Activate Account
(Compensation: Delete User → Cancel Email)
Booking System:
Book Flight → Reserve Hotel → Charge Payment → Send Confirmation
(Compensation: Cancel Flight → Cancel Hotel → Refund Payment)
Best Practices
✅ DO
- Implement compensation for each step
- Use idempotent operations
- Store saga state
- Monitor saga completion
- Test failure scenarios
❌ DON'T
- Don't forget compensation logic
- Don't assume steps always succeed
- Don't skip state persistence
- Don't create circular dependencies