Design a Digital Wallet System
1. Problem Statement & Scope Clarification
System Mission
Design a bank-grade, globally distributed digital wallet backend platform (similar to PayPal, Venmo, and Cash App) capable of supporting instant peer-to-peer (P2P) transfers, multi-currency balances, strict double-entry immutable accounting ledgers, and sub-10 millisecond balance inquiries with absolute mathematical consistency (zero split-brain currency inflation).
Functional Requirements
- Peer-to-Peer (P2P) Transfer (
ExecuteTransfer): Atomically transfer money between two user wallets within a single ACID transaction. - Deposit & Withdrawal (
TopUp/Withdraw): Settle funds between the internal wallet ledger and external banking rails (FedNow, ACH, Stripe). - Double-Entry Immutable Accounting: Every financial operation must record balanced debit and credit entries (); account balances are strictly derived from the audit log.
- Multi-Currency Conversion: Support atomic FX conversion with locked exchange rate quotes.
- Real-Time Balance Inquiry: Retrieve current available, pending, and reserved balances in .
Non-Functional Requirements (SLAs & SLOs)
- Consistency: Strict ACID Serializability. Zero lost updates, zero negative balance overdrafts, zero Phantom reads.
- Availability: uptime SLA for balance queries and transfers.
- Throughput: Support peak throughput of .
- Auditability: 100% immutable transaction ledger with 7-year regulatory retention.
2. Capacity & Scale Estimation (Back-of-the-Envelope Math)
Traffic & Transaction Volume
- Total Registered Wallets: ().
- Daily Active Wallets (DAU): ().
- Daily P2P Transactions: ().
- Average Transfer QPS:
- Peak Transfer QPS ( surge during Black Friday / Super Bowl):
- Read Balance Inquiry QPS:
Storage Footprint (7-Year Regulatory Retention)
Every P2P transfer generates:
- 1 Transaction header record ()
- 2 Ledger entries: 1 Debit + 1 Credit ( each )
- Index & metadata overhead ()
- Total per Transfer: .
This volume fits cleanly within an Amazon Aurora PostgreSQL Multi-AZ Cluster backed by continuous automated replication and tiered S3 Glacier archival.
3. High-Level Architecture & AWS Component Mapping
Interactive Architecture DiagramSynthesizing vector architecture diagram...
4. Double-Entry Accounting Model & Ledger Schema
1. Fundamental Accounting Invariant
In a formal double-entry system, money cannot be created or destroyed. Every transaction must satisfy:
2. Relational PostgreSQL DDL Schema
sql-- 1. Accounts Table (Stores Current Balance Snapshot) CREATE TABLE accounts ( account_id VARCHAR(64) PRIMARY KEY, user_id VARCHAR(64) NOT NULL, currency CHAR(3) NOT NULL DEFAULT 'USD', balance_cents BIGINT NOT NULL DEFAULT 0, locked_cents BIGINT NOT NULL DEFAULT 0, version BIGINT NOT NULL DEFAULT 1, status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT chk_balance_non_negative CHECK (balance_cents >= 0), CONSTRAINT chk_locked_non_negative CHECK (locked_cents >= 0) ); -- 2. Transaction Headers Table CREATE TABLE transactions ( transaction_id VARCHAR(64) PRIMARY KEY, idempotency_key VARCHAR(128) UNIQUE NOT NULL, type VARCHAR(32) NOT NULL, -- P2P_TRANSFER, DEPOSIT, WITHDRAWAL status VARCHAR(16) NOT NULL, -- PENDING, POSTED, REJECTED amount_cents BIGINT NOT NULL, currency CHAR(3) NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- 3. Immutable Double-Entry Ledger Lines Table CREATE TABLE ledger_entries ( entry_id BIGSERIAL PRIMARY KEY, transaction_id VARCHAR(64) NOT NULL REFERENCES transactions(transaction_id), account_id VARCHAR(64) NOT NULL REFERENCES accounts(account_id), direction VARCHAR(6) NOT NULL, -- 'DEBIT' or 'CREDIT' amount_cents BIGINT NOT NULL, currency CHAR(3) NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT chk_positive_amount CHECK (amount_cents > 0) ); CREATE INDEX idx_ledger_account_created ON ledger_entries(account_id, created_at DESC);
5. Core Atomic Transfer Engine & Deadlock Elimination
Deterministic Lexicographical Lock Ordering
When Alice sends 10 to Alice concurrently, arbitrary row locking causes circular PostgreSQL transaction deadlocks:
- Transaction 1: Locks Alice Waits for Bob
- Transaction 2: Locks Bob Waits for Alice DEADLOCK!
Mitigation: Always lock accounts in lexicographically sorted order before updating balances.
sqlBEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 1. Verify Idempotency INSERT INTO transactions (transaction_id, idempotency_key, type, status, amount_cents, currency) VALUES ('tx_998124', 'idemp_key_alice_bob_102', 'P2P_TRANSFER', 'PENDING', 2500, 'USD') ON CONFLICT (idempotency_key) DO NOTHING; -- 2. Lock Source and Destination Rows in Lexicographical Order SELECT account_id, balance_cents FROM accounts WHERE account_id IN ('acc_alice_101', 'acc_bob_202') ORDER BY account_id ASC FOR UPDATE; -- 3. Verify Source Account Sufficiency -- Application verifies Alice has balance >= 2500 cents -- 4. Execute Atomic Balance Mutations UPDATE accounts SET balance_cents = balance_cents - 2500, version = version + 1 WHERE account_id = 'acc_alice_101'; UPDATE accounts SET balance_cents = balance_cents + 2500, version = version + 1 WHERE account_id = 'acc_bob_202'; -- 5. Record Balanced Double-Entry Ledger Entries INSERT INTO ledger_entries (transaction_id, account_id, direction, amount_cents, currency) VALUES ('tx_998124', 'acc_alice_101', 'DEBIT', 2500, 'USD'), ('tx_998124', 'acc_bob_202', 'CREDIT', 2500, 'USD'); -- 6. Finalize Transaction Status UPDATE transactions SET status = 'POSTED' WHERE transaction_id = 'tx_998124'; COMMIT;
Unlock Complete Architecture & Production Runbooks
You have explored the free architectural preview (~47%). Spend 1 Coin to unlock the remaining 6 production deep-dive sections for a full 24 hours.