POFA Data Model Spec v1

2026-11-03 · Status: decided, being implemented · Chinese: 数据模型规范 v1 · Related: Multi-tenant database architecture (commercial stage)

1. Why this spec

A read-only relational audit of the production database on 2026-11-03 found:

This spec only fixes the birth shape of the next real event. It does not restructure the production database or split tables.

2. Canonical fields (event data)

New fieldValuesMeaningLegacy field (kept in sync)
disciplinesingles (doubles reserved)Singles / doubles — how many playnone (new dimension)
draw_typesingle_elimination / double_elimination / round_robinDraw type — how elimination worksformat: single / double / rr
draw_random_seedinteger or nullShuffle seed for random seeding; only applies when seeding=randomseed

Sync rule: the server (POST /api/tournaments and PATCH) keeps both sides in sync — whichever side a client sends, the other is filled in. Existing pages and tools keep reading legacy fields; new code reads only the new fields. The historical value se is read-mapped (= single_elimination) and never written back.

3. Player IDs

4. Locking

5. Score integrity and the DUPR submission gate

6. Core data chain today (at audit time)

LinkState
Event → entriesThe entries table is used only by the MCP flow; live events have zero entry rows
Entries → drawPage flow derives the draw live from the roster; MCP materializes mcp_bracket. Two tracks coexist
Draw → matchesNo matches table; matches are derived results or objects inside mcp_bracket
Matches → scores → advancementImplemented in MCP (scores, winner, auto-advance, cascading undo, mcp_audit); never used on a real event
Completion / archiveMissing; the current end state is the deleted_at soft delete

7. Layered roadmap

Now (this spec): field canonicalization, player_id, locking. All three land in one server choke point; no bulk migration of old data.

Before the first real-event test: entry review and check-in state, withdrawals, referee assignment, scheduling fields (court / scheduled_at), score correction flow.

Commercial stage: multi-tenant organizers, a standalone matches table, DUPR account linking, notifications. Trigger for splitting matches into their own table: one real event run end to end, plus an actual need for cross-match queries or concurrent multi-referee scoring.

Principle: not everything belongs in the event JSON. Scores, entries and player identity will eventually be separate entities — but the timing is decided by evidence from real events, not by imagination.