You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

9.5 KiB

2.3 Game Session Service (Port 8080) - Detailed Implementation Plan

Summary

Implement the Game Session Service as the authoritative runtime engine for quiz gameplay (session lifecycle, timer, attempts, hints, scoring, anti-cheat, and question progression), consistent with the decisions used for Question Bank (2.1) and User Service (2.2).

Runtime stack and conventions:

  • Fiber HTTP service with shared bootstrap and observability.
  • PostgreSQL persistence with EnsureSchema(ctx) startup DDL.
  • Redis for active-session/timer state and fast session guards.
  • Shared backend/shared packages for auth, errors, validation, logging, tracing, metrics, and readiness.

Decisions Reused from 2.1 and 2.2

  1. Service composition pattern:
  • internal/infra/config.FromEnv()
  • logger/metrics/tracer init in cmd/main.go
  • repo init + EnsureSchema(ctx) at startup
  • /health, /ready, /metrics registration
  • route registration via internal/interfaces/http/routes.go
  1. Shared infra usage:
  • PostgreSQL client.
  • Readiness via shared readiness helper with named checks.
  • Zitadel middleware via shared middleware factory.
  1. Error and transport pattern:
  • domain/application errors mapped with shared httputil.SendError.
  • consistent response envelope style used in other services.
  1. Test pyramid:
  • unit tests for domain/application rules
  • HTTP integration tests with in-memory doubles/fakes
  • optional DB-backed integration tests gated by env

Objectives

  1. Provide full game session lifecycle endpoints.
  2. Enforce 30-minute server-side session duration.
  3. Enforce per-question rules: max 3 attempts, hint rules, scoring.
  4. Integrate with Question Bank for question retrieval/answer validation.
  5. Validate authenticated player identity via User Service at session start.
  6. Add anti-cheat checks and audit-friendly session event trail.
  7. Deliver production-ready observability and reliability behavior.

API Endpoints

  • POST /sessions/start
  • POST /sessions/end
  • POST /sessions/:id/answer
  • POST /sessions/:id/hint
  • GET /sessions/:id
  • GET /sessions/:id/question

Auth and Access Rules

  1. All /sessions/* endpoints require authenticated user token.
  2. Caller must own the session (token.sub == session.player_id) or be admin.
  3. Admin override allowed for support/debug operations.
  4. No public gameplay endpoints without auth.

Inter-Service Contracts

User Service dependency

Purpose: validate and hydrate player profile at session start.

  • Call GET /users/:id on start (forward caller bearer token).
  • Expected minimum fields: id, display_name, email_verified.

Decision:

  • hard-block session start when user is not found, soft-deleted, or unauthorized.
  • require email_verified=true to start.

Question Bank dependency

Purpose: serve questions and validate answers.

  • Fetch question: POST /questions/random with exclusion list.
  • Validate answer: POST /questions/:id/validate-answer.

Decision:

  • use HTTP client adapters now.
  • define application interfaces (QuestionBankClient, UserClient) so transport can be replaced with gRPC later without domain changes.

Domain Model

Aggregate:

  • GameSession

Value Objects:

  • SessionStatus (created|active|completed|timed_out|abandoned)
  • AttemptCounter (0..3)
  • SessionTimer (start/end/remaining)
  • QuestionProgress (current question state)

Domain services:

  • ScoringService
  • GameFlowService
  • AntiCheatService

Core invariants

  1. One active session per player.
  2. Session expires at start_time + 30m.
  3. Max attempts per question: 3.
  4. Hint may be requested once per current question.
  5. Correct answer advances to next question.
  6. After 3 incorrect attempts, auto-advance to next question.
  7. No answer/hint after session is completed|timed_out|abandoned.

Data Model (PostgreSQL)

game_sessions

  • id UUID PRIMARY KEY
  • player_id VARCHAR(128) NOT NULL
  • player_name VARCHAR(50) NOT NULL
  • status VARCHAR(16) NOT NULL
  • total_score INT NOT NULL DEFAULT 0
  • questions_asked INT NOT NULL DEFAULT 0
  • questions_correct INT NOT NULL DEFAULT 0
  • hints_used INT NOT NULL DEFAULT 0
  • current_question_id VARCHAR(64) NULL
  • current_attempts INT NOT NULL DEFAULT 0
  • current_hint_used BOOLEAN NOT NULL DEFAULT FALSE
  • question_started_at TIMESTAMPTZ NULL
  • start_time TIMESTAMPTZ NOT NULL
  • end_time TIMESTAMPTZ NULL
  • created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
  • updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()

Indexes:

  • (player_id, status)
  • (status, start_time DESC)
  • (created_at DESC)

session_attempts

  • id UUID PRIMARY KEY
  • session_id UUID NOT NULL
  • question_id VARCHAR(64) NOT NULL
  • attempt_number INT NOT NULL
  • provided_answer VARCHAR(500) NOT NULL
  • is_correct BOOLEAN NOT NULL
  • used_hint BOOLEAN NOT NULL
  • awarded_score INT NOT NULL
  • latency_ms INT NOT NULL
  • created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()

Indexes:

  • (session_id, question_id, attempt_number)
  • (session_id, created_at DESC)

session_events (anti-cheat/audit observability)

  • id UUID PRIMARY KEY
  • session_id UUID NOT NULL
  • event_type VARCHAR(64) NOT NULL
  • metadata_json JSONB NOT NULL DEFAULT '{}'::jsonb
  • created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()

Redis Usage

Keys (prefix gs:):

  • gs:active:{player_id} -> session_id
  • gs:timer:{session_id} -> expiration timestamp + TTL
  • gs:lock:session:{session_id} -> short lock for answer/hint mutation path

Rules:

  1. Redis is used for fast guards and timer checks.
  2. PostgreSQL remains source of truth.
  3. If Redis unavailable:
  • service logs warning
  • gameplay still works using PostgreSQL timing checks
  • readiness reports redis down but not fatal

Endpoint Behavior

POST /sessions/start

Input:

  • optional preferred_theme, optional difficulty. Flow:
  1. auth required; derive player_id from token.
  2. reject if active session exists.
  3. call User Service to confirm profile and email verification.
  4. create session row with active state and timestamps.
  5. fetch first question from Question Bank.
  6. set current_question_id, question_started_at, questions_asked=1.
  7. write Redis active/timer keys. Output:
  • session summary + current question payload.

POST /sessions/:id/answer

Input:

  • answer (1..500 chars). Flow:
  1. auth/ownership check.
  2. lock session (best effort).
  3. ensure session active and not timed out.
  4. anti-cheat latency check.
  5. validate answer via Question Bank.
  6. record attempt.
  7. update scoring.
  8. advance question when correct or max attempts reached.
  9. complete session if no next question. Output:
  • correctness, awarded score, attempts remaining, optional next question, session stats.

POST /sessions/:id/hint

Flow:

  1. auth/ownership + active check.
  2. reject if hint already used.
  3. mark hint usage and increment count.
  4. return hint.

GET /sessions/:id

  • return session status and derived remaining time.

GET /sessions/:id/question

  • return current question without answer.

POST /sessions/end

Input:

  • session_id, optional reason. Flow:
  • auth/ownership check
  • set terminal state
  • clear redis keys
  • write session_events

Anti-Cheat Rules (Initial)

  1. Reject answers before minimum latency threshold.
  2. Reject answer/hint when session not active.
  3. Reject answer/hint when current question missing.
  4. Reject concurrent out-of-order updates using lock.
  5. Log suspicious events (rapid_answer, state_mismatch, expired_session_write).

Package Layout

  • backend/services/game-session-service/cmd/main.go
  • backend/services/game-session-service/internal/infra/config/config.go
  • backend/services/game-session-service/internal/domain/session/
  • backend/services/game-session-service/internal/application/session/
  • backend/services/game-session-service/internal/infra/persistence/ent/
  • backend/services/game-session-service/internal/infra/clients/questionbank/
  • backend/services/game-session-service/internal/infra/clients/usersvc/
  • backend/services/game-session-service/internal/interfaces/http/
  • backend/services/game-session-service/tests/

Configuration

Service-specific:

  • GAME_SESSION_PORT (default 8080)
  • GAME_SESSION_DURATION (default 30m)
  • GAME_SESSION_MAX_ATTEMPTS (default 3)
  • GAME_SESSION_MIN_ANSWER_LATENCY_MS (default 300)
  • GAME_SESSION_LOCK_TTL (default 3s)
  • GAME_SESSION_ACTIVE_KEY_TTL (default 35m)
  • GAME_SESSION_END_REASON_DEFAULT (default abandoned)
  • QUESTION_BANK_BASE_URL (default http://localhost:8081)
  • USER_SERVICE_BASE_URL (default http://localhost:8082)
  • UPSTREAM_HTTP_TIMEOUT (default 3s)

Shared:

  • POSTGRES_*, REDIS_*, TRACING_*, METRICS_*, LOG_LEVEL, ZITADEL_*

Testing Strategy

  1. Unit tests:
  • state transitions
  • scoring matrix
  • attempt/hint limits
  • anti-cheat threshold behavior
  1. HTTP integration tests:
  • start->answer->hint->end flow
  • unauthorized behavior
  • metrics endpoint availability
  1. Repository integration tests (optional env-gated):
  • schema creation
  • transaction behavior
  • active-session constraints

Verification

From backend/services/game-session-service:

go test ./...
go vet ./...

Optional from backend:

go test ./...

Definition of Done

  1. All endpoints implemented and auth/ownership protected.
  2. Session lifecycle, timer, attempts, hint and scoring rules enforced server-side.
  3. Upstream integrations with User Service and Question Bank functional.
  4. /health, /ready, /metrics functional with named readiness checks.
  5. Anti-cheat checks implemented and events logged.
  6. go test ./... and go vet ./... pass for game-session-service.