ADR 0003: Atomic points write

Status:

Accepted

Context

Awarding points touches three rows: the UserPoints record, the wallet balance, and the ledger entry. A partial failure could leave a points row with no wallet effect, or a balance change with no points row.

Decision

_persist_points_wallet_and_transaction (app/services/user_points/persistence.py) opens one session and does all three writes with auto_commit=False, committing once at the end and rolling back on any exception. The repository create(..., auto_commit=False) contract is what lets a service compose several writes into a single transaction; transaction boundaries live in the service layer, not the repository.

Consequences

  • The point-assignment write is all-or-nothing: no torn state.

  • The service layer is the only place that should compose multi-row writes.

  • This atomicity covers assignment only. The points-to-coins conversion path is not yet wrapped in a single transaction - see Known limitations.

See also