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.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

  1. 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 /metrics endpoints.
  • Route registration through internal/interfaces/http/RegisterRoutes.
  1. 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.
  1. Use the same persistence style:
  • PostgreSQL with pgxpool and SQL DDL in repository EnsureSchema, matching current pragmatic pattern.
  1. 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

  1. Provide player registration and profile management APIs.
  2. Keep local profile data synchronized with authenticated identity claims from Zitadel.
  3. Support GDPR rights: data export and account deletion.
  4. Enforce RBAC on admin endpoints and ownership checks on user-scoped endpoints.
  5. Deliver production-ready observability and baseline test coverage.

API Endpoints

  • POST /users/register
  • GET /users/:id
  • PUT /users/:id
  • DELETE /users/:id
  • POST /users/verify-email
  • GET /admin/users
  • POST /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 KEY
  • zitadel_user_id VARCHAR(128) UNIQUE NULL
  • email VARCHAR(320) UNIQUE NOT NULL
  • email_verified BOOLEAN NOT NULL DEFAULT FALSE
  • display_name VARCHAR(50) NOT NULL
  • consent_version VARCHAR(32) NOT NULL
  • consent_given_at TIMESTAMPTZ NOT NULL
  • consent_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 NULL
  • INDEX(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

  1. GET/PUT/DELETE /users/:id:
  • Allowed for the same authenticated user (sub mapped to local user) or admin role.
  1. POST /users/register:
  • Requires authenticated token.
  • If user already exists by zitadel_user_id or email, return idempotent success (200) with existing profile.
  1. 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.
  1. Admin routes (/admin/*):
  • Require admin role + MFA via shared Zitadel middleware behavior.

Endpoint Behavior

  1. 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.
  1. GET /users/:id
  • Return sanitized profile (no internal audit metadata).
  • 404 for missing or soft-deleted user.
  1. PUT /users/:id
  • Allow updates to display_name and consent refresh fields.
  • Reject immutable field updates (email, id, zitadel_user_id).
  1. DELETE /users/:id (GDPR)
  • Soft-delete local profile.
  • Trigger best-effort anonymization policy for user-facing displays.
  • Write compliance audit event.
  1. POST /users/verify-email
  • Refresh verification status from Zitadel and persist local email_verified=true with timestamp/audit event.
  1. GET /admin/users
  • Paginated listing with filters: email, display_name, created_after, created_before, include_deleted.
  1. 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.go
  • backend/services/user-service/internal/infra/config/config.go
  • backend/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

  1. Add internal/infra/config/config.go with:
  • USER_SERVICE_PORT (default 8082)
  • USER_ADMIN_LIST_DEFAULT_LIMIT (default 50)
  • USER_ADMIN_LIST_MAX_LIMIT (default 200)
  • shared POSTGRES_*, TRACING_*, METRICS_*, LOG_LEVEL, ZITADEL_*
  1. Update cmd/main.go to 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

  1. Define domain entity, value objects, domain errors.
  2. Define application DTOs for request/response mapping.
  3. Implement application service methods:
  • Register
  • GetProfile
  • UpdateProfile
  • DeleteUser
  • VerifyEmail
  • AdminListUsers
  • AdminExportUser
  1. Reuse shared error codes and transport error mapping style from question-bank handlers.

Workstream C - Persistence Layer

  1. Implement Client and UserRepository under internal/infra/persistence/ent.
  2. Implement EnsureSchema(ctx) for users and user_audit_log tables/indexes.
  3. Add SQL queries for pagination/filtering and soft-delete aware reads.
  4. Add mappers between SQL rows and domain entity.

Workstream D - HTTP Interface

  1. Add request/response models with validation tags.
  2. Implement handler methods with consistent JSON envelope and status codes.
  3. Register routes in routes.go with admin group and middleware support.
  4. Enforce ownership checks using user id from auth context.

Workstream E - Compliance and Audit

  1. Implement export builder (deterministic JSON schema).
  2. Implement delete flow with audit logging and redaction policy hooks.
  3. Ensure admin export/delete actions are auditable.

Workstream F - Testing

  1. Unit tests:
  • email/display-name/consent validation
  • idempotent registration logic
  • ownership and admin authorization guard behavior
  1. HTTP integration tests (in-memory repo + test middleware):
  • register/get/update/delete happy paths
  • unauthorized/forbidden cases
  • admin list/export auth behavior
  • /metrics availability
  1. 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 failures
  • 401: missing/invalid token
  • 403: non-owner/non-admin access
  • 404: user not found
  • 409: duplicate email or identity conflict
  • 500: unexpected errors

Observability

  1. Structured logs:
  • include service=user-service, request id, actor id (when available), and route.
  • never log raw tokens or PII-heavy payloads.
  1. Metrics:
  • request count/latency/status via shared HTTP metrics.
  • dedicated counters for user_registration_total, user_deletion_total, gdpr_export_total.
  1. Tracing:
  • ensure startup/shutdown tracer lifecycle identical to question-bank.
  • instrument service/repository boundaries for registration/export/delete paths.

Security and Privacy Controls

  1. Input validation and sanitization on all mutable fields.
  2. No trust of client-supplied identity attributes when token context exists.
  3. Soft-delete as default deletion strategy; avoid hard delete in initial implementation.
  4. Minimize data in responses and logs (data minimization principle).
  5. Audit every admin export and delete action.

Delivery Sequence (3-4 Days)

  1. Day 1: bootstrap/config + domain/application skeleton + schema DDL.
  2. Day 2: repository + register/get/update endpoints + auth/ownership checks.
  3. Day 3: delete/verify-email/admin list/export + observability wiring.
  4. 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

  1. All endpoints implemented and route-protected per spec.
  2. PostgreSQL schema auto-created on startup and validated in tests.
  3. Health, readiness, and metrics endpoints functional.
  4. AuthN/AuthZ and ownership checks covered by tests.
  5. GDPR export/delete flows implemented with audit records.
  6. go test ./... and go vet ./... pass for user-service.