fintech
Account Ledger
Money moved between two accounts, or not moved at all.
A transfer is two changes that must both happen: one balance down, one up, and a pair of ledger entries recording why. DynamoDB has no rollback, so the only way to make that safe is to send it as one transaction and let the database refuse the whole thing.
The model
Balance
What an account currently holds, in minor units.
- pk
- ACCOUNT#<accountId>
- sk
- BALANCE
Attributes: pence (N), currency (S), updatedAt (N)
LedgerEntry
One immutable line: which account, which direction, how much.
- pk
- ACCOUNT#<accountId>
- sk
- ENTRY#<sequence>
Attributes: pence (N), direction (S), transferId (S), at (N)
Access patterns
- QueryRead an account
The balance and the entries that produced it, in one read.
Design notes
There is no rollback to fall back onwhy
Write the debit, then fail before the credit, and the money is gone from one account and arrived nowhere. Nothing undoes it - DynamoDB has no transaction to abort after the fact, so compensating for a half-applied transfer means writing the reversal yourself and hoping that write succeeds. TransactWriteItems avoids the situation rather than recovering from it.
The guard rides along in the same transactionwhy
The debit carries a condition that the balance is at least the amount, so an overdraft is refused as part of the same all-or-nothing write. Checking the balance first would reopen the gap the transaction closes: between the check and the transfer, another transfer can spend the same money.
Balance and entries in one partitiontrade-off
BALANCE sorts before every ENTRY# for an account, so a statement is one Query returning the current figure followed by the history. The entries are the record of truth and the balance is a running total kept beside them - which is why a transfer writes both rather than deriving one from the other on read.
This one the preview genuinely cannot runtrade-off
Unlike most designs here, this is not the resolver's limitation. The wasm engine does not implement TransactWriteItems at all - it answers 'not supported' - so the transfer is not offered as a run at all rather than offered and failing. Every access pattern this page lists is one you can actually press. The transfer itself is four items in one write: debit, credit, and a ledger entry each way.
Taught in the course
- Claim it, or fail - A ConditionExpression lets a write happen only if the data still allows it.
The balance and the entries that produced it, in one read.
Run an operation to see the raw engine response.
The in-browser engine is a preview build. Transactions, vector search, streams, tags and TTL are among the operations it doesn't implement yet.