9.8 KiB
2.2 User Service (Port 8082) - Detailed Implementation Plan
Summary
Implement the User Service as the source of truth for player profile and compliance data (registration, profile updates, email verification status, GDPR export/deletion), aligned with the implementation decisions already used in backend/services/question-bank-service.
This service will run on Fiber, persist in PostgreSQL, expose health/readiness/metrics, and use shared packages for auth, validation, error mapping, logging, tracing, and service bootstrapping.
Decisions Reused from 2.1 Question Bank Service
- Use the same service composition style as
question-bank-service/cmd/main.go:
internal/infra/config.FromEnv()for service-specific env parsing.- Shared logger/metrics/tracer initialization from
backend/shared. - Repository initialization +
EnsureSchema(ctx)at startup. /health,/ready, and/metricsendpoints.- Route registration through
internal/interfaces/http/RegisterRoutes.
- Use the same auth approach:
- Public routes can remain open when Zitadel env is missing (local development fallback).
- Admin routes use Zitadel JWT middleware when configured.
- Use the same persistence style:
- PostgreSQL with
pgxpooland SQL DDL in repositoryEnsureSchema, matching current pragmatic pattern.
- Follow the same testing pyramid:
- Domain/application unit tests.
- HTTP integration tests with in-memory doubles.
- Optional DB-backed integration tests gated by environment.
Objectives
- Provide player registration and profile management APIs.
- Keep local profile data synchronized with authenticated identity claims from Zitadel.
- Support GDPR rights: data export and account deletion.
- Enforce RBAC on admin endpoints and ownership checks on user-scoped endpoints.
- Deliver production-ready observability and baseline test coverage.
API Endpoints
POST /users/registerGET /users/:idPUT /users/:idDELETE /users/:idPOST /users/verify-emailGET /admin/usersPOST /admin/users/:id/export
Domain Model
Aggregate:
User
Value objects:
Email(normalized lowercase, RFC-like format validation)DisplayName(2-50 chars, allowed chars policy aligned with project guidelines)ConsentRecord(terms/privacy/version + timestamp + source)
Repository contract (UserRepository):
Create(ctx, user)GetByID(ctx, id)GetByEmail(ctx, email)UpdateProfile(ctx, id, patch)MarkEmailVerified(ctx, id, verifiedAt)SoftDelete(ctx, id, deletedAt)List(ctx, pagination, filters)ExportBundle(ctx, id)
Data Model (PostgreSQL)
Primary table: users
id UUID PRIMARY KEYzitadel_user_id VARCHAR(128) UNIQUE NULLemail VARCHAR(320) UNIQUE NOT NULLemail_verified BOOLEAN NOT NULL DEFAULT FALSEdisplay_name VARCHAR(50) NOT NULLconsent_version VARCHAR(32) NOT NULLconsent_given_at TIMESTAMPTZ NOT NULLconsent_source VARCHAR(32) NOT NULL DEFAULT 'web'created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()deleted_at TIMESTAMPTZ NULL
Indexes:
UNIQUE(email)UNIQUE(zitadel_user_id) WHERE zitadel_user_id IS NOT NULLINDEX(deleted_at)INDEX(created_at DESC)
Audit table: user_audit_log
- Track admin exports and delete actions for compliance/auditability.
- Fields:
id,actor_user_id,target_user_id,action,metadata_json,created_at.
Authorization and Identity Rules
GET/PUT/DELETE /users/:id:
- Allowed for the same authenticated user (
submapped to local user) oradminrole.
POST /users/register:
- Requires authenticated token.
- If user already exists by
zitadel_user_idor email, return idempotent success (200) with existing profile.
POST /users/verify-email:
- Requires authenticated token.
- Resolve email verification from trusted source (token claim and/or Zitadel userinfo).
- Never accept arbitrary email-verified flag from client body.
- Admin routes (
/admin/*):
- Require admin role + MFA via shared Zitadel middleware behavior.
Endpoint Behavior
POST /users/register
- Input:
display_name,consent_version,consent_source. - Identity fields (
email,zitadel_user_id) come from token / Zitadel client, not request body. - Output: canonical user profile.
GET /users/:id
- Return sanitized profile (no internal audit metadata).
404for missing or soft-deleted user.
PUT /users/:id
- Allow updates to
display_nameand consent refresh fields. - Reject immutable field updates (
email,id,zitadel_user_id).
DELETE /users/:id(GDPR)
- Soft-delete local profile.
- Trigger best-effort anonymization policy for user-facing displays.
- Write compliance audit event.
POST /users/verify-email
- Refresh verification status from Zitadel and persist local
email_verified=truewith timestamp/audit event.
GET /admin/users
- Paginated listing with filters:
email,display_name,created_after,created_before,include_deleted.
POST /admin/users/:id/export
- Produce structured JSON export bundle (profile + consents + audit entries).
- Return payload inline for now (later storage handoff can be added).
Package and File Layout
Target structure (mirrors 2.1 service organization):
backend/services/user-service/cmd/main.gobackend/services/user-service/internal/infra/config/config.gobackend/services/user-service/internal/domain/user/backend/services/user-service/internal/application/user/backend/services/user-service/internal/infra/persistence/ent/backend/services/user-service/internal/interfaces/http/backend/services/user-service/tests/
Implementation Work Breakdown
Workstream A - Bootstrap and Configuration
- Add
internal/infra/config/config.gowith:
USER_SERVICE_PORT(default8082)USER_ADMIN_LIST_DEFAULT_LIMIT(default50)USER_ADMIN_LIST_MAX_LIMIT(default200)- shared
POSTGRES_*,TRACING_*,METRICS_*,LOG_LEVEL,ZITADEL_*
- Update
cmd/main.goto match question-bank startup pattern:
- logger/metrics/tracer creation
- postgres client + schema ensure
- readiness check endpoint
- metrics endpoint
- route registration with optional admin middleware
Workstream B - Domain and Application Layer
- Define domain entity, value objects, domain errors.
- Define application DTOs for request/response mapping.
- Implement application service methods:
RegisterGetProfileUpdateProfileDeleteUserVerifyEmailAdminListUsersAdminExportUser
- Reuse shared error codes and transport error mapping style from question-bank handlers.
Workstream C - Persistence Layer
- Implement
ClientandUserRepositoryunderinternal/infra/persistence/ent. - Implement
EnsureSchema(ctx)forusersanduser_audit_logtables/indexes. - Add SQL queries for pagination/filtering and soft-delete aware reads.
- Add mappers between SQL rows and domain entity.
Workstream D - HTTP Interface
- Add request/response models with validation tags.
- Implement handler methods with consistent JSON envelope and status codes.
- Register routes in
routes.gowith admin group and middleware support. - Enforce ownership checks using user id from auth context.
Workstream E - Compliance and Audit
- Implement export builder (deterministic JSON schema).
- Implement delete flow with audit logging and redaction policy hooks.
- Ensure admin export/delete actions are auditable.
Workstream F - Testing
- Unit tests:
- email/display-name/consent validation
- idempotent registration logic
- ownership and admin authorization guard behavior
- HTTP integration tests (in-memory repo + test middleware):
- register/get/update/delete happy paths
- unauthorized/forbidden cases
- admin list/export auth behavior
/metricsavailability
- Repository integration tests (optional env-gated):
- schema creation
- unique constraints
- pagination/filter correctness
Error Handling Contract
Use project shared domain errors and consistent mappings:
400: validation failures401: missing/invalid token403: non-owner/non-admin access404: user not found409: duplicate email or identity conflict500: unexpected errors
Observability
- Structured logs:
- include
service=user-service, request id, actor id (when available), and route. - never log raw tokens or PII-heavy payloads.
- Metrics:
- request count/latency/status via shared HTTP metrics.
- dedicated counters for
user_registration_total,user_deletion_total,gdpr_export_total.
- Tracing:
- ensure startup/shutdown tracer lifecycle identical to question-bank.
- instrument service/repository boundaries for registration/export/delete paths.
Security and Privacy Controls
- Input validation and sanitization on all mutable fields.
- No trust of client-supplied identity attributes when token context exists.
- Soft-delete as default deletion strategy; avoid hard delete in initial implementation.
- Minimize data in responses and logs (data minimization principle).
- Audit every admin export and delete action.
Delivery Sequence (3-4 Days)
- Day 1: bootstrap/config + domain/application skeleton + schema DDL.
- Day 2: repository + register/get/update endpoints + auth/ownership checks.
- Day 3: delete/verify-email/admin list/export + observability wiring.
- Day 4: tests, bug fixes, and readiness verification.
Verification Commands
From backend/services/user-service:
go test ./...
go vet ./...
From backend (optional full workspace check):
go test ./...
Definition of Done
- All endpoints implemented and route-protected per spec.
- PostgreSQL schema auto-created on startup and validated in tests.
- Health, readiness, and metrics endpoints functional.
- AuthN/AuthZ and ownership checks covered by tests.
- GDPR export/delete flows implemented with audit records.
go test ./...andgo vet ./...pass foruser-service.