harden: bound BankAccount dedup state + snapshot-safe codec #59

Closed
opened 2026-07-04 12:11:57 +00:00 by momsse · 1 comment
Owner

Migré depuis viziertronic/octant#126 — ouvert le 2026-06-27 par @momsse.

#54 dedups detected transactions by tracking knownExternalTransactionIds (a HashSet) in BankAccountState. Two residual concerns from that review:

  • Unbounded state: the set retains every external id ever detected and is rebuilt from the full event log on each load (no snapshot). For a long-lived high-volume account this grows linearly and slows rehydration. Options: bound the dedup window (e.g. retain only ids within the relevant value-date range), or move dedup to a UNIQUE constraint on the bank-transaction stream / read model ((bank_account_id, external_transaction_id)) so BankTransactionCreationSaga is idempotent and BankAccount need not carry every id — same write-side-guard philosophy as #58.
  • Snapshot serialization (latent): no PostgresSnapshotStore is wired for BankAccount today, but if one is added its state codec must encode the HashSet with Schema.HashSet (a plain JSON codec turns it into {}, silently re-detecting every transaction on restore).

Surfaced in the #54 review.

> _Migré depuis [viziertronic/octant#126](https://github.com/viziertronic/octant/issues/126) — ouvert le 2026-06-27 par @momsse._ #54 dedups detected transactions by tracking `knownExternalTransactionIds` (a `HashSet`) in `BankAccountState`. Two residual concerns from that review: - **Unbounded state**: the set retains every external id ever detected and is rebuilt from the full event log on each load (no snapshot). For a long-lived high-volume account this grows linearly and slows rehydration. Options: bound the dedup window (e.g. retain only ids within the relevant value-date range), or move dedup to a **UNIQUE constraint** on the bank-transaction stream / read model (`(bank_account_id, external_transaction_id)`) so `BankTransactionCreationSaga` is idempotent and `BankAccount` need not carry every id — same write-side-guard philosophy as #58. - **Snapshot serialization (latent)**: no `PostgresSnapshotStore` is wired for `BankAccount` today, but if one is added its state codec must encode the `HashSet` with `Schema.HashSet` (a plain JSON codec turns it into `{}`, silently re-detecting every transaction on restore). Surfaced in the #54 review.
Author
Owner

@momsse — 2026-06-27 (commentaire migré) :

Note from #127 review: the HashSet<ExternalTransactionId> codec is a hard prerequisite for snapshotting, not a separable follow-up — PostgresSnapshotStore.make requires a Schema.Codec<BankAccountState> at construction. So this issue must add the Schema.HashSet codec as part of wiring any BankAccount snapshot store.

> _@momsse — 2026-06-27 (commentaire migré) :_ Note from #127 review: the `HashSet<ExternalTransactionId>` codec is a hard prerequisite for snapshotting, not a separable follow-up — `PostgresSnapshotStore.make` requires a `Schema.Codec<BankAccountState>` at construction. So this issue must add the `Schema.HashSet` codec *as part of* wiring any BankAccount snapshot store.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
momsse/octant#59
No description provided.