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.
287 lines
9.5 KiB
Markdown
287 lines
9.5 KiB
Markdown
# 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`
|
|
|
|
2. Shared infra usage:
|
|
- PostgreSQL client.
|
|
- Readiness via shared readiness helper with named checks.
|
|
- Zitadel middleware via shared middleware factory.
|
|
|
|
3. Error and transport pattern:
|
|
- domain/application errors mapped with shared `httputil.SendError`.
|
|
- consistent response envelope style used in other services.
|
|
|
|
4. 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
|
|
|
|
2. HTTP integration tests:
|
|
- start->answer->hint->end flow
|
|
- unauthorized behavior
|
|
- metrics endpoint availability
|
|
|
|
3. Repository integration tests (optional env-gated):
|
|
- schema creation
|
|
- transaction behavior
|
|
- active-session constraints
|
|
|
|
## Verification
|
|
From `backend/services/game-session-service`:
|
|
```bash
|
|
go test ./...
|
|
go vet ./...
|
|
```
|
|
|
|
Optional from `backend`:
|
|
```bash
|
|
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.
|