The first move in any interview: define requirements and sketch the API before drawing a single box.
POST /v1/wallets { user_id } → { wallet_id, balance }GET /v1/wallets/{id} → { balance, currency }POST /v1/transfers { from, to, amount, idempotency_key } → { transfer_id, status }POST /v1/deposits { wallet_id, amount, psp_ref, idempotency_key } → { status }GET /v1/wallets/{id}/ledger?cursor= → [{ entry... }]You send $50 from your wallet to a friend's. Somewhere between 'debit me' and 'credit them', a service crashes. Did the money vanish? Did it land twice? A digital wallet's entire job is to make sure the answer is always neither. Among system design topics, this is the one where correctness is non-negotiable: every bug is somebody's money.
Here is the whole thing in plain terms. Each user has a wallet balance. A transfer is not one write; it is two: debit the sender and credit the receiver. Those two steps may live on different shards or even different services, so you cannot wrap them in a single database transaction. Instead you run a saga: try the debit, try the credit, and if either step fails, run a compensating action that undoes the earlier step. The ledger records every movement as double-entry bookkeeping (one debit, one matching credit), so you can prove the books still balance.
The pattern that makes this auditable is event sourcing plus CQRS. The write path appends immutable events (WalletDebited, WalletCredited, TransferFailed) to an event store. The read path projects those events into a balance query model optimized for 'what is my balance?'. If a projection goes wrong, you rebuild it by replaying the event log. That is reproducibility: the history is the source of truth, not the current row.
Interviewers love comparing distributed transaction options. Two-phase commit (2PC) is strong but blocks and is a poor fit for high-latency wallet services. TCC (Try-Confirm-Cancel) reserves funds first, then confirms or cancels. Saga with compensations is usually the practical choice for wallets: each local step is an ACID transaction, and failure triggers an explicit undo. This system teaches why money needs a ledger, how sagas replace distributed locks, and why an event log beats 'update the balance row' as your audit trail.