20 KiB
User Service Implementation Plan
Planning Principles
This plan is aligned with the current repository architecture and is written for an experienced middle-level Go developer implementing an internal trusted microservice.
Execution priorities:
- preserve the frozen auth and geo ownership boundaries
- keep user state authoritative in one service
- prefer explicit command behavior over generic patch behavior
- keep synchronous read paths simple for auth and lobby
- separate current effective state from append-only or historical records where fast reads matter
- keep the first version storage-agnostic at the domain boundary even if Redis is the initial backend
Stage 01 — Freeze Vocabulary, Contracts, and Cross-Service Ownership
Goal
Remove naming ambiguity and freeze the service boundary before implementation.
Tasks
- Freeze the regular-user-only scope of
User Service. - Freeze that system-admin identity is out of scope and belongs to later
Admin Service. - Freeze the self-service vocabulary:
GetMyAccountUpdateMyProfileUpdateMySettings
- Freeze that
race_namereplacesdisplay_name. - Freeze the current ownership split for
declared_country:- current value in
User Service - workflow and history in
Geo Profile Service
- current value in
- Freeze the auth-facing internal REST endpoints already reserved by
Auth / Session Service. - Freeze the need for create-only registration context on
EnsureUserByEmail.
Deliverables
- service README with stable terminology
- short internal ADR or equivalent note for
declared_countryownership split - short internal ADR or equivalent note for regular-user versus admin identity split
Exit Criteria
- no unresolved naming conflict remains around
race_name,declared_country, entitlement, sanction, or limit semantics - no service boundary question remains open for auth, lobby, or geo
Targeted Tests
- none yet beyond documentation review
Stage 02 — Define Domain Entities and Redis-Backed Logical State
Goal
Describe the persistent state clearly enough that storage adapters can be built without revisiting core semantics.
Tasks
- Define logical entities for:
- user account
- race-name reservation
- blocked e-mail subject
- entitlement period record
- current entitlement snapshot
- sanction record
- limit record
- Freeze required fields, timestamps, and identifiers for each entity.
- Decide Redis logical key layout and lookup indexes without leaking them into the domain layer.
- Freeze deterministic pagination keys for admin listing.
- Define how active/effective evaluation works for sanctions and limits.
Deliverables
- domain entity definitions
- storage design notes for Redis keys and secondary indexes
- active/effective evaluation rules
Exit Criteria
- every required read and mutation can map to a clear logical entity set
- Redis adapters can be implemented directly from the frozen logical model
Targeted Tests
- domain validation tests for required fields
- tests for effective-state evaluation of active versus expired records
Stage 03 — Implement Auth-Facing Resolution, Ensure, Existence, and E-Mail Blocking
Goal
Provide the minimum trusted API needed by Auth / Session Service.
Tasks
- Implement:
- resolve by e-mail
- ensure by e-mail
- exists by user id
- block by user id
- block by e-mail
- Preserve exact route shapes already reserved by the auth REST client.
- Implement the separate blocked-email-subject model.
- Make
BlockByEmailidempotent for both existing-user and no-user cases. - Ensure
ResolveByEmailandEnsureUserByEmailboth respect blocked-email subjects.
Deliverables
- trusted internal REST handlers for auth-facing endpoints
- domain services for resolution and block behavior
- Redis-backed storage for user existence and blocked-email subjects
Exit Criteria
- auth can distinguish
existing,creatable, andblocked - blocked e-mail subjects prevent user creation before a user exists
BlockByUserIDandBlockByEmailare idempotent
Targeted Tests
- resolve existing/creatable/blocked by e-mail
- ensure existing/created/blocked outcomes
- blocked e-mail subject prevents creation before user record exists
- block by user id on unknown user returns not found
- repeated block calls stay idempotent
Stage 04 — Add New-User Creation Context from Auth
Goal
Support first-login user creation with initial settings captured at confirm time.
Tasks
- Extend
EnsureUserByEmailcontract with create-only registration context:preferred_languagetime_zone
- Validate
preferred_languageas BCP 47. - Validate
time_zoneas IANA TZ name. - Generate initial
race_nameinplayer-<shortid>form during creation. - Initialize the newly created user with:
- free entitlement
- no active sanctions
- no custom limits
- Ignore registration context for existing users.
- Document required follow-up changes in
gatewayandauthsession.
Deliverables
- extended ensure-by-email request model
- create-user domain service
- generated-race-name helper
Exit Criteria
- first successful ensure-create path can fully initialize a new user
- existing-user ensure does not overwrite language or time zone
Targeted Tests
- new user created with generated
race_name, derivedpreferred_language, and required clienttime_zone - existing user ensure ignores create-only registration context
- invalid BCP 47 or IANA inputs are rejected on create path
Stage 05 — Implement Self-Service Account Read and Split Profile/Settings Mutations
Goal
Expose the minimal authenticated account surface routed by Edge Gateway.
Tasks
- Implement
GetMyAccount. - Implement
UpdateMyProfileforrace_nameonly. - Implement
UpdateMySettingsfor:preferred_languagetime_zone
- Ensure
GetMyAccountreturns:- account identity fields
- current entitlement snapshot
- active sanctions
- active effective limits
- read-only
declared_country
- Reject attempts to mutate
emailordeclared_countrythrough self-service flows. - Enforce
profile_update_blocksanction on both self-service mutations.
Deliverables
- authenticated application services for account read and updates
- gateway-facing handler or adapter contracts for future routing
- DTOs for account aggregate and mutation requests
Exit Criteria
- authenticated users can read current account state in one aggregate
- profile and settings changes are clearly separated
- self-service updates cannot mutate forbidden fields
Targeted Tests
GetMyAccountreturns current entitlement, active sanctions, active limits, and read-onlydeclared_countryUpdateMyProfilecannot change email ordeclared_countryUpdateMySettingsvalidates BCP 47 and IANA values- active
profile_update_blockdenies both update flows
Stage 06 — Implement race_name Uniqueness Policy Behind a Dedicated Interface
Goal
Keep race_name uniqueness strict and replaceable.
Tasks
- Introduce a dedicated race-name policy interface.
- Implement canonicalization for uniqueness checks:
- case-insensitive folding
- confusable anti-fraud normalization
- Add Redis-backed reservation storage for canonicalized keys.
- Preserve original casing for stored and returned
race_name. - Ensure rename flow handles reservation swap safely.
- Keep the interface narrow so a future shared name-catalog service can replace the local implementation.
Deliverables
- race-name policy interface
- local normalization implementation
- reservation adapter and conflict handling
Exit Criteria
- no two users can hold conflicting
race_namevalues under the frozen policy - self-service rename is atomic with respect to uniqueness reservation
Targeted Tests
- uniqueness rejects case-insensitive collisions
- uniqueness rejects common anti-fraud-confusable collisions
- rename releases the old reservation only after the new one is secured
- failed reservation backend causes mutation to fail closed
Stage 07 — Implement Entitlement History Plus Materialized Current Snapshot
Goal
Support both auditability and fast synchronous entitlement reads.
Tasks
- Implement period-based entitlement history records.
- Implement a materialized current entitlement snapshot.
- Define the v1 plan catalog:
freepaid_monthlypaid_yearlypaid_lifetime
- Implement explicit trusted entitlement commands:
- grant paid access
- extend paid access
- revoke paid access
- Update current snapshot transactionally with each successful entitlement mutation.
- Ensure the default new-user path creates the correct free snapshot.
Deliverables
- entitlement domain model
- history store
- current snapshot store
- trusted entitlement command handlers
Exit Criteria
- current effective entitlement is always readable without replaying history
- history and snapshot stay consistent across supported mutation paths
Targeted Tests
- entitlement period mutations update the materialized current snapshot correctly
- free default is created for new users
- extending or revoking access preserves deterministic current-state behavior
Stage 08 — Implement Sanctions and Limit Records with Active/Effective Evaluation
Goal
Support negative policy and quota overrides without scattering policy logic into consumers.
Tasks
- Implement sanction records with optional expiry.
- Implement limit records with numeric values and optional expiry.
- Freeze v1 sanction catalog:
login_blockprivate_game_create_blockprivate_game_manage_blockgame_join_blockprofile_update_block
- Freeze v1 limit catalog:
max_owned_private_gamesmax_active_private_gamesmax_pending_public_applicationsmax_pending_private_join_requestsmax_pending_private_invites_sentmax_active_game_memberships
- Implement active/effective evaluation with current time.
- Implement trusted explicit commands to apply/remove sanctions and set/remove limits.
Deliverables
- sanction model and store
- limit model and store
- effective-state evaluator
- trusted mutation handlers
Exit Criteria
- active sanctions and active limits can be read consistently from one user account view
- expired or removed records are not treated as active
Targeted Tests
- active sanctions appear in account reads
- expired sanctions and limits stop affecting effective state
- applying and removing sanctions/limits is idempotent where appropriate
Stage 09 — Implement Lobby Eligibility Snapshot API
Goal
Give Game Lobby one synchronous read that contains everything it needs for
user-level access decisions.
Tasks
- Design and implement one trusted query by
user_id. - Return:
- existence
- current entitlement snapshot
- active lobby-relevant sanctions
- effective lobby-relevant limits
- derived booleans for lobby decisions
- Keep the response read-optimized so lobby does not need multiple dependent
calls back into
User Service. - Define deterministic not-found behavior.
Deliverables
- lobby eligibility query endpoint
- response DTO
- mapping from entitlement/sanction/limit state to derived eligibility fields
Exit Criteria
Game Lobbycan decide create/join/manage eligibility from one read- no extra fan-out to other user sub-queries is required
Targeted Tests
- lobby eligibility snapshot reflects paid status, sanctions, and limits
- unknown user returns stable not-found behavior
- derived booleans remain consistent with raw effective state
Stage 10 — Implement Geo declared_country Sync Command
Goal
Support the current-country denormalization path owned by Geo Profile Service.
Tasks
- Implement one explicit trusted command to sync current
declared_country. - Validate ISO alpha-2 input.
- Ensure the command updates only the current value on the user account.
- Do not add country history behavior to
User Service. - Preserve explicit not-found behavior for unknown
user_id. - Emit the corresponding auxiliary declared-country change event after a successful commit.
Deliverables
- geo-facing sync endpoint
- application service for country sync
- event publication on successful mutation
Exit Criteria
- geo can synchronize current
declared_countrywithout introducing hidden history inUser Service - unknown users are rejected deterministically
Targeted Tests
- geo country sync changes only current
declared_country - invalid country codes are rejected
- country sync emits the correct auxiliary event after commit
Stage 11 — Implement Admin Lookup, Filtered Listing, and Explicit Trusted Mutations
Goal
Provide the operational surface required by future Admin Service and manual
operations.
Tasks
- Implement exact reads by:
user_id- normalized
email - exact
race_name
- Implement paginated listing with richer filters:
- paid/free state
- paid expiry
- current
declared_country - sanction code
- limit code
- eligibility markers
- Freeze deterministic ordering for the listing.
- Implement the explicit trusted command surface for:
- entitlement grant/extend/revoke
- sanction apply/remove
- limit set/remove
- declared-country sync
- Preserve audit metadata on every trusted mutation.
Deliverables
- admin/internal read endpoints
- filtered listing endpoint
- explicit trusted mutation endpoints
Exit Criteria
- future
Admin Servicecan operate fully through this trusted API without needing direct storage access - list filtering and pagination are deterministic
Targeted Tests
- admin listing filters behave deterministically
- exact lookups by
user_id, email, andrace_nameresolve the correct user - every trusted mutation preserves actor and reason metadata
Stage 12 — Add Per-Domain-Area Async Events and Observability
Goal
Make production behavior observable without treating events as the source of truth.
Tasks
- Publish per-domain-area events for:
- profile changes
- settings changes
- entitlement changes
- sanction changes
- limit changes
- declared-country changes
- Add structured logs for trusted mutations and critical failures.
- Add metrics for:
- auth-facing resolution outcomes
- user creation outcomes
- race-name reservation conflicts
- entitlement mutation outcomes
- sanction and limit mutation outcomes
- event publication failures
- Add tracing spans on synchronous internal request paths where useful.
Deliverables
- event publisher integration
- structured logging hooks
- metrics and tracing instrumentation
Exit Criteria
- mutation flows are observable in production without ad hoc logging
- event publication failure does not compromise source-of-truth persistence
Targeted Tests
- async event publication failure does not lose source-of-truth state
- event payloads include minimum required metadata
- observability hooks do not change business behavior
Stage 13 — Add Contract Tests Against Auth, Lobby, and Geo Expectations
Goal
Verify the service not only in isolation, but against the internal contracts it must satisfy for other services.
Tasks
- Add compatibility tests against the frozen auth-facing REST contract.
- Add compatibility tests for the future ensure-by-email registration context.
- Add lobby eligibility snapshot contract tests.
- Add geo country-sync contract tests.
- Add account aggregate tests matching gateway-routed user expectations.
- Add tests for deterministic admin listing filters and ordering.
Deliverables
- cross-service contract test suite
- test fixtures for auth/lobby/geo integration expectations
Exit Criteria
- no ambiguity remains about service behavior expected by auth, lobby, or geo
- regressions in reserved internal contract shapes are caught automatically
Targeted Tests
- new user created on first successful confirm with generated
race_name, derivedpreferred_language, and required clienttime_zone - existing user confirm ignores create-only registration context
- blocked e-mail subject prevents user creation before a user record exists
GetMyAccountreturns current entitlement, active sanctions, active limits, and read-onlydeclared_country- lobby eligibility snapshot reflects paid status, sanctions, and limits
- geo country sync changes only current
declared_country
Stage 14 — Add Rollout Notes for Gateway/Auth/OpenAPI Updates and Shared geoip
Goal
Prepare the surrounding platform changes required for the service to work in its intended end-to-end form.
Tasks
- Document the required
gatewaypublicconfirm-email-codeaddition oftime_zone. - Document the required
authsessionpublic OpenAPI mirror change. - Document the required
authsession -> userensure contract extension for create-only registration context. - Document the required shared
pkg/geoippackage for gateway and geo. - Document README follow-up updates needed in
gatewayandgeoprofile. - Define rollout order so the cross-service contract changes do not land in an unsafe sequence.
Deliverables
- rollout checklist
- dependency order notes
- cross-repo or cross-module follow-up ticket list
Exit Criteria
- the implementation can be integrated into surrounding services without rediscovering hidden dependencies
- no required upstream or downstream change is left implicit
Targeted Tests
- documentation review only
Recommended First Working Slice
The smallest useful end-to-end slice is:
- Stage 01
- Stage 02
- Stage 03
- Stage 04
This slice makes it possible to support auth-driven user creation and blocking before the rest of the service surface exists.
Recommended Second Slice
The next highest-value slice is:
- Stage 05
- Stage 06
- Stage 07
- Stage 08
- Stage 09
This slice gives the platform usable account reads, self-service profile and settings updates, and the lobby eligibility integration.
Final Acceptance Criteria
The first production-capable v1 of User Service should satisfy all of the
following:
- new users can be created through auth with generated
race_name, derivedpreferred_language, and required clienttime_zone - existing-user auth confirm ignores create-only registration context
- blocked e-mail subjects prevent new-user creation before a user record exists
race_nameuniqueness rejects case-insensitive and anti-fraud-confusable collisionsGetMyAccountreturns current entitlement, active sanctions, active limits, and read-onlydeclared_countryUpdateMyProfilecannot change email ordeclared_countryUpdateMySettingsvalidates BCP 47 and IANA values- entitlement period mutations update the materialized current snapshot correctly
- lobby eligibility snapshot reflects paid status, sanctions, and limits
- geo
declared_countrysync changes only current account state - admin listing filters and ordering are deterministic
- async event publication failure does not lose source-of-truth state
Implementation Order Summary
Recommended implementation order:
- freeze vocabulary and ownership
- define domain entities and logical storage
- build auth-facing resolution and blocking
- add new-user creation context
- build self-service account read and updates
- add race-name uniqueness policy
- build entitlement history and current snapshot
- build sanctions and limits
- add lobby eligibility snapshot
- add geo country sync
- add admin reads, listing, and mutations
- add events and observability
- add cross-service contract tests
- document and sequence rollout dependencies