How a solo developer built a reliable wallet engine with Kafka and Postgres
Tunde Oladejo published a detailed architecture for a fintech wallet using a double-entry ledger and the transactional outbox pattern to guarantee data integrity without distributed transactions.…
Tunde Oladejo published a detailed architecture for a fintech wallet using a double-entry ledger and the transactional outbox pattern to guarantee data integrity without distributed transactions.
Building a fintech product requires treating the ledger as sacrosanct. The most difficult part is not moving money, but ensuring it is never lost to concurrency errors or network failures. Developer Tunde Oladejo published a detailed technical design for a wallet engine that prioritizes reliability and auditability using event-driven architecture.
The system is designed to handle credits, debits, and transfers while guaranteeing that no value is created or destroyed. It maintains a full audit trail and uses events to notify downstream services like analytics or webhooks. The architecture provides a playbook for building durable financial infrastructure at an independent scale.
A four-part architecture
Oladejo’s design uses four primary components. PostgreSQL serves as the absolute source of truth for all wallets, ledger entries, and transactions. Apache Kafka handles asynchronous event distribution to other services. Redis provides locks for idempotency, preventing duplicate requests from processing. The application layer, built on the NestJS framework in TypeScript, orchestrates the business logic.
The core principle is to separate the database transaction, which is the boundary of correctness, from external communication. The system never publishes an event to Kafka from within the main business logic transaction. This separation is key to preventing the system from entering an inconsistent state where a database write succeeds but the corresponding event fails to send.
Double-entry ledger for correctness
The foundation of the system’s financial integrity is a double-entry bookkeeping model. Every financial operation creates exactly two ledger entries: a debit from a source wallet and a credit to a destination wallet. The sum of these two entries is always zero. This ensures that money is only ever moved, never created or destroyed.
By enforcing this rule at the database level, the system guarantees balance sheet consistency. Any transaction that fails to create a corresponding pair of entries is rolled back. This provides a complete, auditable history of every value transfer within the system, which is a non-negotiable requirement for most financial applications.
The transactional outbox pattern
To ensure events are reliably delivered, the design uses the transactional outbox pattern. When a financial transaction occurs, the application logic performs two writes within a single atomic database transaction. First, it writes the business data (e.g., the debit and credit ledger entries). Second, it writes a record of the event to be published into a dedicated outbox table in the same database.
A separate process then reads from this outbox table and reliably publishes the event to Kafka, retrying on failure. Because both the business data and the event record are written in the same transaction, the system avoids partial failures. The event is guaranteed to be published if, and only if, the financial transaction was successfully committed to the database.
WHAT WE'D CHANGE
The architecture Oladejo describes is robust, but its specific implementation details present trade-offs that may not suit every project. The outbox relay mechanism, which polls the database every two seconds, is a primary candidate for modification.
Polling is simple but introduces latency and can be inefficient at scale. A more modern approach is to use Change Data Capture (CDC). A tool like Debezium can monitor the database’s transaction log and stream changes from the outbox table directly to Kafka. This provides near-real-time event publishing with significantly lower overhead than constant polling. It is a direct upgrade to the relay component.
For a truly indie-scale or early-stage product, Kafka itself might be overkill. Operating a Kafka cluster introduces significant complexity. Simpler, managed message queues like AWS SQS or even PostgreSQL's native LISTEN/NOTIFY functionality could achieve the same reliability for the outbox pattern with a lower operational burden. The choice hinges on the expected transaction volume and the team's familiarity with distributed systems.
LANDING
The technical choices in this design are not just about engineering elegance. They are a direct investment in the product's core value proposition: trust. A wallet engine that can mathematically prove its correctness and guarantee its operations provides the foundation for a durable fintech business. For founders building in this space, reliability is not an added feature. It is the entire product.
The investor read
This architecture signals a founder with high technical acumen who understands the core risks of their domain. Building with patterns like double-entry ledgers and transactional outboxes de-risks the technology from day one, which is a critical diligence item for any fintech investment. While the stack (Kafka, NestJS) is robust, it suggests a deliberate, bootstrapped approach focused on correctness over rapid, fragile feature development. This is not a quick flip; it's a foundation for a durable business. For a pre-seed or seed investor, this level of engineering rigor in a solo founder is a strong positive signal about capital efficiency and the potential for building a product that can earn and maintain user trust, a key moat in financial services.
Pull quote: “A separate process then reads from this outbox table and reliably publishes the event to Kafka, retrying on failure.”
Every claim ties to a primary source. See our methodology.