Store the ledger, not the balance
A balance is a cache. Treat it like one and reconciliation stops being a monthly panic.
Balances lie
A balance column updated in place has no memory. When it is wrong, and it will be, there is nothing to replay. An append-only ledger of entries can be summed at any point in time and audited line by line.
Double entry is not optional
Every movement debits one account and credits another, in one transaction. The books always balance because the schema makes it impossible for them not to.
select account_id, sum(amount) as balance
from ledger_entries
where posted_at <= $1
group by account_id;Then cache
Materialise balances for speed if you need to, but derive them from the ledger and rebuild them when they disagree. The ledger is the truth. The balance is a convenience.