advanced~2h

System Design with Transactions

Every pattern from Parts 1–8, applied to the exact systems interviewers ask about most: UPI, wallets, inventory, and ride-sharing. This chapter is where the theory becomes design-round-ready.

◆ HLD — the core requirement

A payment between two different banks (UPI) is the purest real-world instance of Chapter 15's distributed transaction problem: your bank's ledger and the recipient's bank's ledger are two entirely separate systems that have never shared a database.

Design decisionPattern applied
Debit sender, credit receiver, across two banksSaga (Chapter 17) with orchestration — a central switch (like NPCI in India's real UPI network) explicitly coordinates both legs.
What if the credit leg fails after the debit succeeded?Compensating transaction: automatic reversal/refund credit back to the sender — this is exactly why UPI failures sometimes show "amount debited, refund in progress" instead of an instant, atomic-looking failure.
Ensuring a retried debit request doesn't double-debitIdempotency (Chapter 17 §6), keyed by a unique transaction reference ID generated client-side.
Recording the payment event durably before notifying anyoneOutbox Pattern (Chapter 18) — the ledger update and the "payment initiated" event are committed together locally, then relayed.

◆ HLD — the core requirement

Chapter 01 §1's warehouse race condition, at scale: thousands of users may try to book the same limited inventory (concert seats, flash-sale stock) within the same second.

Design decisionPattern applied
Preventing overselling under high contentionPessimistic locking (Chapter 07 §2), SELECT... FOR UPDATE on the specific inventory row — deliberately chosen over optimistic locking here because contention is high and expected, not rare.
Enforcing "stock can never go negative" as a hard guaranteeA database CHECK constraint (Chapter 03 §4) as the final, unbypassable safety net, beneath the application-level check.
Holding a seat temporarily while the user completes paymentA time-boxed "reservation" state with an expiry (a scheduled job or TTL-based mechanism releases unconfirmed reservations) — this is itself a small saga: reserve → pay → confirm, or reserve → timeout → release.

◆ HLD — the core requirement

Matching a rider to a driver, starting a trip, and settling payment at the end each touch different services (Matching, Trip, Payment, Driver Wallet) — and a trip that completes but never settles payment to the driver is a real, damaging failure mode.

Design decisionPattern applied
Trip completion → fare calculation → rider charge → driver payoutSaga, likely orchestrated given the linear, well-defined step sequence (Chapter 17 §5's guidance for complex, many-step flows).
A driver's earnings ledger updating correctly despite retries/failuresIdempotent, inbox-pattern-style (Chapter 18 §4) payout processing keyed by trip ID.
Rider and driver apps both needing near-real-time trip statusEvent-driven fan-out (Chapter 19's Kafka patterns) from the Trip service, rather than each app polling every other service directly.

◆ HLD — the core requirement

A claim moves through multiple independent stages (submission, medical review, fraud check, approval, payout) often over days, not milliseconds — a genuinely long-running business process, not a single request's worth of work.

Design decisionPattern applied
A multi-day, multi-team workflow with clear, auditable state transitionsOrchestrated saga (Chapter 17 §4) is almost mandatory here — the explicit, readable flow control matters enormously for auditability and debugging a process that can pause for days at a human review step.
Never losing track of a claim's current state, even across service restartsEvery state transition durably persisted before triggering the next step — directly Chapter 05's Durability principle, applied to business process state, not just row data.

▲ Common mistake in system design interviews

Reaching for 2PC (Chapter 16) as the answer to any of these scenarios is a common interview red flag — every one of them is better served by Saga plus Outbox plus idempotency, precisely because 2PC's blocking, availability-limiting nature (Chapter 16 §3) directly conflicts with what all four of these systems actually need: high availability and independent service scaling, at the cost of eventual (not immediate) consistency that the compensating-transaction pattern handles gracefully.

Question to askChapter to reach for
Does this operation touch more than one database/service?15 — recognize the distributed problem exists at all
What's the compensation for each step, and can every step actually be undone?17 — Saga design
How do I atomically record a change and its corresponding event?18 — Outbox
What happens if a step (or a whole saga) runs twice?17 §6 / 18 §4 — idempotency, inbox
Is contention on a shared resource high or low?07 — pessimistic vs. optimistic locking
What isolation level does this specific operation actually need?04 — isolation levels, chosen per operation, not globally

Want a visual for this concept?

Generate a diagram tailored to “System Design with Transactions” — the AI picks whichever visual (flowchart, comparison, sequence, etc.) best fits.

Sign in to generate a visual →

Practice quiz

Next Step

Continue to Production Engineering← Back to all Transaction Mastery chapters