Skip to content

Game

A Game represents a board game in the Stratastax catalogue: enough information to record and interpret Matches against it, and to display it meaningfully in the archive. Where this data originally came from — a sync from BoardGameGeek, or any other future source — is an infrastructure concern, not part of this domain model. Game itself has no knowledge of any external source.


A Game is responsible for:

  • providing a display identity (name, description, image);
  • defining the valid player-count range for a Match;
  • carrying a set of Tags (categories/mechanisms) for discovery and grouping;
  • defining how a Match’s results should be interpreted — its ScoreType and, where relevant, its WinCondition — so that ranking can be computed consistently;
  • owning the set of Expansions available for it.

A Game can have any number of Expansions — additional content that extends the base game, such as “Everdell: Pearlbrook” or “Everdell: Legendary Cards” for the base game “Everdell”. An Expansion always belongs to exactly one base Game; it has no independent meaning outside of it.

Expansions are what a Match’s Playset selects from when recording which content was used in a given play (see Playset). Game only owns and exposes the available Expansions; it plays no role in which ones a specific Match actually used.

  • Name — required (e.g. “Pearlbrook”).
  • Description — optional.
  • ImageUrl — optional.

A Game is NOT responsible for:

  • sourcing or syncing its own data, or that of its Expansions, from any external catalogue (BGG or otherwise) — that is an infrastructure/ application concern, external to this model;
  • holding Playset or Loadout data — those are Match-specific and player-specific respectively, not properties of the Game itself;
  • computing a Match’s ranking — Game only supplies the ScoreType and WinCondition that ranking logic reads;
  • deciding which of its Expansions a specific Match used — that is recorded on the Match’s Playset.

  • Name — required.
  • Description — required.
  • MinPlayers / MaxPlayers — required; MinPlayers >= 1 and MaxPlayers >= MinPlayers.
  • ImageUrl — optional.
  • Tags — zero or more free-form labels covering both categories and mechanisms (e.g. “Engine Building”, “Area Control”), deliberately not modeled as a fixed taxonomy.
  • ScoreType — one of:
  • Score — a numeric value; ranking depends on WinCondition.
  • Time — a duration; ranking depends on WinCondition.
  • Turns — a turn count; ranking depends on WinCondition.
  • WinLoss — a binary outcome per MatchParticipant; no WinCondition needed.
  • WinCondition — HigherWins or LowerWins; required when ScoreType is Score, Time, or Turns, and meaningless when ScoreType is WinLoss.

  • MinPlayers >= 1 and MaxPlayers >= MinPlayers.
  • WinCondition is present if and only if ScoreType is Score, Time, or Turns.
  • A Game’s ScoreType and WinCondition determine how every MatchParticipant result for Matches referencing it should be ranked; this model does not allow a Match to override its Game’s interpretation.
  • An Expansion belongs to exactly one Game, for its entire lifetime.

A Game exists once it can be referenced by a Match — whether it was pre-populated by an import process ahead of time, or added at the moment a User first records a match against it. This model treats both as equivalent; which one actually happens is an implementation decision entirely outside this model.

A Game’s display attributes (Name, Description, ImageUrl, Tags) may be updated over time, e.g. by a catalogue refresh. New Expansions may be added to a Game at any time. Its ScoreType and WinCondition should be treated as stable once Matches exist against it, since changing them would change how existing historical results are interpreted.

A Game is never deleted while referenced by any Match. An Expansion is never deleted while referenced by any Playset.


  • Game is referenced by Match.
  • Game owns Expansion (0..N).
  • Expansion is referenced by Playset.

None. Game is reference/catalogue data, not a process aggregate. A domain event exists so that something else can react to it; nothing in this model needs to react to a Game (or an Expansion) simply coming into existence — it is a side effect of an import or first-use process, not a meaningful business decision. If a genuine downstream need for reacting to catalogue changes emerges later, events can be introduced then.


Game is its own Aggregate Root, independent of Match. Expansion is a child entity within the Game aggregate — it has no identity or lifecycle independent of its Game. Game has no dependency on how or when it, or any of its Expansions, was populated — any import mechanism is a client of this model, not part of it, and no reference to any external source (BGG or otherwise) exists anywhere in this model.