Domain Model

Who is this page for?

Integrators who need to know what each object means on the wire, and contributors who need the schema. The canonical machine-readable ERD (Mermaid) lives in docs/domain-model.md; this page is the prose reference.

The big picture

GAME’s entities fall into five clusters. Solid arrows below read “parent → many children”:

AUTH / ACCESS            CAMPAIGN HIERARCHY
┌──────────┐             ┌──────────┐
│OAuthUsers│◄──owns────► │  Games   │──┬──► GameParams   (key/value config)
└────┬─────┘   ┌────────►└────┬─────┘  └──► Tasks ──► TaskParams
     │         │              │
     ▼         │              ▼
┌──────────┐   │        ┌──────────────┐
│  ApiKey  │───┘        │UserGameConfig│  (enrolment + experiment group)
└────┬─────┘            └──────────────┘
     │ audit trail: every entity records the ApiKey that created it
     ▼
PARTICIPATION & SCORING                 ECONOMY
┌──────────┐                            ┌──────────┐
│  Users   │──► UserActions             │  Wallet  │──► WalletTransactions
│          │──► UserInteractions        └────▲─────┘
│          │──► UserPoints ◄──awards──┐       │ one wallet per user
│          │──────────────────────────┴───────┘
└──────────┘
             STRATEGY                    OBSERVABILITY
┌────────────────────┐                  ApiRequests · Logs · KpiMetrics
│ StrategyDefinition │──► versions      UptimeLogs · StrategyExecutionLog
│ (custom DSL)       │                  AbuseLimitCounter · ExportAuditLog
└────────────────────┘

Core entities

Identity & access

Entity

Meaning

OAuthUsers

A human/admin identity federated from Keycloak (provider + provider_user_id). Owns API keys and is the principal behind OAuth2-authenticated requests.

ApiKey

A machine credential (apiKey, client, active) issued to a caller. Every write records which key created it, giving a complete audit trail and the basis for per-key data scoping.

Campaign hierarchy

Entity

Meaning

Games

A campaign/project. Carries an externalGameId (your identifier), a platform, and a strategyId - the default scoring strategy for its tasks. If unset, the built-in default strategy applies.

GameParams

Key/value configuration attached to a game (e.g. base points). These feed strategy variables.

Tasks

A concrete activity inside a game (externalTaskId). A task inherits the game’s strategy unless it declares its own strategyId. Tasks are independent of each other.

TaskParams

Key/value configuration attached to a task; overrides/extends game params for that task’s scoring.

Participation & scoring

Entity

Meaning

Users

A participant, keyed by your externalUserId. Users are created lazily on first scoring if they don’t exist yet.

UserGameConfig

A user’s enrolment in a game, including their experimentGroup (e.g. control vs. treatment for A/B studies) and per-user configData.

UserActions

An explicit event a user performed (typeAction + structured data), recorded for audit and as scoring input.

UserInteractions

A typed interaction between a user and a task (interactionType/interactionDetail).

UserPoints

The heart of the system: one row per award. Holds points, the caseName (why the rule matched), an idempotencyKey (dedupes retries), the originating data, and links to user + task.

Economy

Entity

Meaning

Wallet

Exactly one per user. Tracks pointsBalance and coinsBalance plus the conversionRate (points per coin).

WalletTransactions

An append-only ledger of wallet movements (transactionType, points, coins, appliedConversionRate), so balances are always reconstructable.

Strategy & observability

Entity

Meaning

StrategyDefinition

A custom, DSL-authored strategy and its versions. Saving a published version creates a new DRAFT rather than overwriting (see Strategies). Referenced by strategyId = custom:<uuid>.

StrategyExecutionLog

A sampled trace of a custom-strategy run: status, latency, node count, case name, error code, and (bounded) node-by-node trace. Drives the observability dashboard.

ApiRequests / Logs / KpiMetrics / UptimeLogs

Operational telemetry: per-request records, structured application logs, daily KPI rollups, and uptime samples.

AbuseLimitCounter

Backing store for the database rate-limiter (scope, window, counter). See Security.

ExportAuditLog

Records data-export operations for compliance/audit.

The BaseModel contract

Every persisted entity inherits a common base (app/model/base_model.py) that contributes four columns to every table:

Column

Type

Notes

id

UUID

Primary key, server-generated.

created_at

datetime (tz-aware UTC)

Set on insert. Stored as timezone-aware UTC.

updated_at

datetime (tz-aware UTC)

Refreshed on update.

apiKey_used / oauth_user_id

FK / string

The credential that performed the write - the audit + scoping anchor.

Because apiKey_used is stamped on every write, GAME can answer “who created this?” for any row and can scope reads so one API key only sees the data it created (admins bypass scoping). See Security.

Identifiers: internal vs. external

GAME maintains a clean separation between its identifiers and yours:

  • Internal ids are UUID primary keys (gameId, taskId, userId). They appear in URLs that address a specific record (/games/{gameId}/...).

  • External ids are the strings you supply (externalGameId, externalTaskId, externalUserId). They let you address records using your own domain keys and keep GAME loosely coupled to your system.

A common integration pattern: create a game and keep the returned gameId; thereafter reference tasks and users by your external ids.

Idempotency & concurrency

  • Point assignment accepts an idempotency key (request header, surfaced into the event data as eventId). A repeated request with the same key does not create a second UserPoints row - safe to retry.

  • Writes are transactional; wallet movement and points persistence happen together so a partial failure does not leave a points row without its wallet effect.

Where to go next