Welcome to our inaugural blog post. In this series, we'll explore a range of topics including our development challenges as well as financial infrastructure with a particular focus on cross-border payments.
I'm Wassily the founder of nxos, and since this is our kickoff, let me start with a quick introduction. Previously, I led the engineering team at a rapidly growing fintech company in Dubai. When I joined, we were a small team of five, but we expanded to nearly 200 in just four years. During that time, we scaled from zero to processing transactions worth well over a billion.
At nxos, we're transforming this experience into as-simple-as-possible, scalable fintech infrastructure. Our focus includes ledgers, reconciliation, card integrations, and, in particular, cross-border payments.
Needless to say, if your company needs fintech solutions in ledgers, reconciliation, card integrations, or cross-border payments, reach out to me at [email protected] to explore how nxos can help.
Today, we'll build a simple double-entry ledger using PostgreSQL, featuring source and destination accounts. Before we build out the solution, let's clarify the key concepts that underpin this model:
This model naturally adheres to the double-entry standard: the amount deducted from your employer's account exactly matches the amount deposited into your bank account.
If you'd like to further explore double-entry accounting and general accounting principles, here are two resources I've found particularly helpful:
For our source-destination model, we'll create a single table named 'move'. We chose this name because it represents the movement of an amount from the source to the destination. The table structure is as follows:
CREATE TABLE move (
id SERIAL PRIMARY KEY,
source VARCHAR(255) NOT NULL,
destination VARCHAR(255) NOT NULL,
amount BIGINT NOT NULL,
currency VARCHAR(3) NOT NULL,
description TEXT NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- Indexes to list transactions for sender and receiver
CREATE INDEX idx_move_sender ON move(source, created_at);
CREATE INDEX idx_move_receiver ON move(destination, created_at);
For each move, we define a sender and a destination. The sender's balance decreases by the sent amount, while the destination's balance increases by the same amount, making their sum zero.
The source
and destination
columns reference accounts
in the system. An account in this case is simply an entity that can hold a currency and have a balance. There can be a one-to-one mapping between an account and a customer, or a customer can have many accounts. For example, a customer might have both a checking account and a savings account.
Each 'move' includes an amount
and a currency
field. The amount
represents the quantity of currency in subunits to be transferred from the source to the destination account. In our ledger, amounts are denoted in subunits, representing the smallest possible unit of the currency (e.g., cents for USD). This approach avoids floating-point arithmetic issues.