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 decision | Pattern applied |
|---|---|
| Debit sender, credit receiver, across two banks | Saga (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-debit | Idempotency (Chapter 17 §6), keyed by a unique transaction reference ID generated client-side. |
| Recording the payment event durably before notifying anyone | Outbox 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 decision | Pattern applied |
|---|---|
| Preventing overselling under high contention | Pessimistic 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 guarantee | A 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 payment | A 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 decision | Pattern applied |
|---|---|
| Trip completion → fare calculation → rider charge → driver payout | Saga, 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/failures | Idempotent, inbox-pattern-style (Chapter 18 §4) payout processing keyed by trip ID. |
| Rider and driver apps both needing near-real-time trip status | Event-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 decision | Pattern applied |
|---|---|
| A multi-day, multi-team workflow with clear, auditable state transitions | Orchestrated 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 restarts | Every 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 ask | Chapter 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 →