accesspatterns.dev
← All models

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.
account-ledger
Try an example

The balance and the entries that produced it, in one read.

PK(pk)
SK(sk)
currency
pence
updatedAt
at
direction
transferId
ACCOUNT#a-1044
BALANCES
GBPS
182450N
1786648320N
ACCOUNT#a-1044
ENTRY#000003S
60000N
1785888000N
creditS
tr-87004S
ACCOUNT#a-1044
ENTRY#000004S
2500N
1786648320N
debitS
tr-88120S
ACCOUNT#a-2071
BALANCES
GBPS
9930N
1786648320N
ACCOUNT#a-2071
ENTRY#000010S
60000N
1785888000N
debitS
tr-87004S
ACCOUNT#a-2071
ENTRY#000011S
2500N
1786648320N
creditS
tr-88120S

Run an operation to see the raw engine response.

table account-ledgerkeys PK / SKitems 6

The in-browser engine is a preview build. Transactions, vector search, streams, tags and TTL are among the operations it doesn't implement yet.