Digital Wallet System Design | Bank Account | SAGA pattern
4 different implementations of distributed transactions for wallet System Design
Welcome to the 62 new subscribers who have joined us since last week.
If you aren’t subscribed yet, join 1000+ engineers and technical managers learning Advanced System Design.
Digital wallets sit at the heart of modern payments, powering apps like Venmo, Cash App, and PayPal. At their core, wallets must do something deceptively simple: debit one account, credit another, and ensure balances are always correct.
Today, we’re going to design Digital Wallet and cover the following components:
Transactions in a Single Database (Small Scale)
Distributed Transactions with a Sharded Database
Distributed Transactions with a Saga Pattern
Event Sourcing Approach
Transactions in a Single Database (Small Scale)
This is the simplest and most intuitive design. All accounts, balances, and transactions live in a single relational database. Each wallet operation (like transferring $10 from Alice to Bob) is wrapped in a database transaction, so both debit and credit happen together or not at all.
Why it works well:
Strong consistency by default (ACID transactions).
Easy to reason about and reconcile.
Great for small teams and MVPs.
Trade-offs:
Limited scalability — one database can only handle so many writes per second.
Contention on “hot accounts” (like a merchant receiving thousands of small payments).
A single point of failure unless you invest in replication/failover.
👉 Use this approach when you’re starting small and need correctness above all else.
Distributed Transactions with a Sharded Database
As traffic grows, one database won’t cut it. The next step is to shard accounts across multiple databases (for example, by user ID). To transfer money between accounts on different shards, you need a transaction coordinator that runs a two-phase commit (2PC) to ensure atomicity across shards.
Why it works well:
Horizontally scalable for large user bases.
Maintains strong consistency across shards.
Trade-offs:
Two-phase commit is slow and fragile under failures.
The transaction coordinator can become a bottleneck.
Operational complexity increases — debugging stuck or partially committed transactions is non-trivial.
👉 Use this approach when you must maintain strict consistency across accounts but also need to scale beyond a single database.
Distributed Transactions with a Saga Pattern
At very large scale, two-phase commit becomes impractical. Instead, you can use the Saga pattern, which breaks a global transaction into a series of smaller local ones. For example:
Debit Alice’s account (on shard A).
Publish an event.
Credit Bob’s account (on shard B).
If step 3 fails, run a compensation (credit Alice back).
Why it works well:
Avoids global locks and coordinators, making it highly scalable.
Resilient to partial failures — the system can recover using compensating actions.
Trade-offs:
Only eventually consistent: there may be a short window where Alice has been debited but Bob not yet credited.
Designing compensations is tricky — not all operations can be easily “undone.”
Requires careful user experience design (e.g., showing “pending” states).
👉 Use this approach when high availability and scalability matter more than strict atomicity. Most large-scale fintech systems end up here.
📣 I enjoyed reading the week
How Cloudflare Was Able to Support 55 Million Requests per Second With Only 15 Postgres Clusters on System Design Newsletter by
- Technics to get the maximum out of your Postgres cluster.
Check out a more detailed video coverage on my Youtube.
Thank you for your continued support of my newsletter and the growth to a 1k+ members community 🙏