Roadmap › Microservices & System Design with .NET › Module: Data and Consistency
Lessons in this course (9)
5. Database per service, sagas and the outbox pattern
Advanced★ Must knowLesson 5 of 9
Database per service: each service owns its data; others access it only via the service's API or events. This keeps services independently deployable — but you lose cross-service ACID transactions and joins.
Saga — a long business transaction made of local transactions, each with a compensating action if a later step fails:
text
PlaceOrder: Orders: create (Pending) → Payment: charge → Inventory: reserve → Orders: confirm
Failure at Inventory: Payment: refund (compensate) → Orders: cancel- Choreography — services react to each other's events (simple, but the flow is implicit).
- Orchestration — a coordinator (MassTransit state machine, Durable Functions) tells each service what to do (explicit, easier to monitor).
Transactional outbox — solves "saved to DB but failed to publish the event" (dual write):
csharp
await using var tx = await db.Database.BeginTransactionAsync();
db.Orders.Add(order);
db.Outbox.Add(new OutboxMessage("OrderPlaced", JsonSerializer.Serialize(new OrderPlaced(order.Id, order.Total))));
await db.SaveChangesAsync();
await tx.CommitAsync();
// A background worker reads unsent Outbox rows, publishes them, marks them sent (MassTransit/NServiceBus have this built in).Inbox / idempotent consumer — store processed message ids so duplicates are ignored.
CAP theorem (short version): during a network partition a distributed system must choose Consistency or Availability. Most microservice systems choose availability + eventual consistency for cross-service data.