ADR-0006: Event-first data model
ADR-0006: Event-first data model
- Status: Accepted
- Date: 2026-07-30
- Supersedes: —
- Superseded by: —
Context
Two reference points. Goodreads is shelf-first and bolted rereads on badly — still one rating and one review per book, so the record of how your opinion changed is destroyed on every update. Letterboxd is event-first: each diary entry carries its own rating, older ratings are preserved, and the film-level rating is a projection of the most recent one.
Decision
Three layers, not two:
games— catalogue entry (designers, mechanics, weight, player count)shelf_entries— a user’s standing relationship to a game (owned / wishlist, current rating, review)sessions— one individual play, with participants and outcome
Ratings live on the session. The shelf entry’s rating is a projection of the most recent session rating. Older session ratings are never overwritten — opinion changing over time is the point of the product, not an edge case.
players are not users. A player at the table may never have an account. players is a lightweight local record with an optional user_id and a claim-and-merge flow.
Consequences
- The shelf rating is derived state. Any write path that sets it directly, rather than projecting from sessions, is a bug.
- Never assume a session participant has a user record. Every query joining participants must tolerate a null
user_id. - Most people’s game groups will not all sign up, so the Player/User split is load-bearing rather than theoretical. Retrofitting it is very painful; adding it now costs one nullable foreign key.
- Enforced by test: a second session rating must update the shelf projection and leave the first session’s rating intact. This is the M1 acceptance invariant.