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.