Skip to content

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:

  1. games — catalogue entry (designers, mechanics, weight, player count)
  2. shelf_entries — a user’s standing relationship to a game (owned / wishlist, current rating, review)
  3. 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.