receipt

Transactions &
Sagas

In a monolith, BEGIN TRANSACTION and COMMIT are magic. In microservices, they don't exist. How do you ensure money is deducted AND the order is shipped?

ACID in a Distributed World

broken_image

The Problem

You split your monolith into an Order Service (Postgres) and a Payment Service (Stripe/Redis).

1. Order Service: Create Order #123 (Success)
2. Payment Service: Charge Card (Failed: Insufficient Funds)
// Now you have an order without payment. Database rollback is impossible across services.

You cannot have strict ACID across network boundaries without significant performance penalties.

Two-Phase Commit (2PC)

The traditional "heavy" solution. A Coordinator asks everyone to prepare, then commit.

Phase 1: Prepare

Coordinator asks A & B: "Can you commit?"

A & B lock resources and reply "Yes".

Phase 2: Commit

If ALL say "Yes", Coordinator says "Commit".

If ANY say "No", Coordinator says "Rollback".

blockWhy we hate it

  • Blocking: Locks are held during the network round-trips. Slows everything down.
  • Single Point of Failure: If Coordinator dies, everyone is stuck waiting.
  • Not Cloud Native: Hard to implement with microservices/NoSQL.

The Saga Pattern

history

Long-Lived Transactions

Instead of one big lock, break the transaction into a sequence of local steps. If a step fails, execute Compensating Actions to undo previous steps.

Happy Path

1. Order Created
arrow_downward
2. Payment Charged
arrow_downward
3. Inventory Reserved

Failure Path

1. Order Created
arrow_downward
2. Payment Failed!
undo
3. Cancel Order (Compensation)

Result: Eventual Consistency. The system is temporarily inconsistent, but eventually settles.

Orchestration vs Choreography

Choreography (Events)

Decentralized

Services listen to events and decide what to do. No central boss.
"OrderCreated" → Payment listens & charges → "PaymentProcessed" → Inventory listens...

  • ✓ Loose coupling.
  • ✗ Hard to track workflow (Mental complexity).

Orchestration (Command)

Centralized

A central Saga Orchestrator (State Machine) tells services what to do.
Orchestrator calls Order service. If OK, calls Payment service...

  • ✓ Easy to debug & visualize state.
  • ✗ Tighter coupling (Orchestrator must know flow).

Idempotency (Crucial)

Why you need it

In a distributed system, networks retry. You might receive the "Charge Payment" message twice.You must not charge the customer twice.

Idempotency Key

Pass a unique ID with every request (e.g., `order_id` or `uuid`).

IF (key exists in DB) {
  return "Already Done";
} ELSE {
  process();
  save(key);
}