slate-test-1770505673/.sdlc/features/user-preferences/spec.md
rdev-worker 6db90ac66d
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
build: /spec-feature user-preferences --requirements 'CRUD API for user pref...
2026-02-07 23:15:27 +00:00

4.8 KiB

Feature: User Preferences API

Problem Statement

Users need a way to persist and retrieve their application preferences (theme, language, notification settings) across sessions and devices. Currently, no preference storage exists — any customization is lost when a user logs out or switches devices. The preferences-api service exists as a scaffold with example CRUD patterns but has no preference-specific domain logic.

User Stories

  • As an authenticated user, I want to save my theme preference so that the UI matches my preference across sessions.
  • As an authenticated user, I want to set my preferred language so that the application displays in my chosen locale.
  • As an authenticated user, I want to configure my notification settings so that I only receive alerts I care about.
  • As a frontend application, I want to retrieve all preferences for a user in a single request so that the UI can be initialized efficiently.
  • As an authenticated user, I want to update individual preferences without overwriting all others so that partial updates are safe.

Acceptance Criteria

  • GET /api/preferences-api/preferences/{user_id} returns all preferences for a user as key-value pairs in the {data, meta} envelope
  • PUT /api/preferences-api/preferences/{user_id} creates or updates preferences for a user (upsert semantics)
  • Supported preference keys: theme (values: light, dark, system), language (ISO 639-1 codes, e.g. en, fr, es), notifications_enabled (boolean as string: true, false)
  • Unknown preference keys are rejected with a 400 Bad Request and descriptive error
  • Invalid preference values are rejected with a 400 Bad Request and descriptive error (e.g., theme: "blue" is invalid)
  • PUT is idempotent — sending the same request twice produces the same result
  • PUT supports partial updates — sending only {"theme": "dark"} does not clear language or notifications_enabled
  • GET for a user with no preferences returns 200 with an empty object {} (not 404)
  • Both endpoints require authentication via JWT (auth middleware)
  • Authenticated users can only access their own preferences (user_id in path must match JWT subject), returning 403 Forbidden otherwise
  • Preferences are persisted in PostgreSQL with a migration
  • OpenAPI spec documents both endpoints with request/response schemas
  • Handler tests cover success, validation errors, not-found, and authorization cases
  • Service-layer tests cover business logic with mock repository
  • Response times < 50ms at p99 for single-user preference reads

Technical Constraints

  • Must follow existing hexagonal architecture: domain → service → port (interface) → adapter (implementation)
  • Handlers must return error, wrapped with app.Wrap()
  • Request binding must use app.Bind() or app.BindAndValidate()
  • Errors must use httperror.BadRequest, httperror.NotFound, httperror.Forbidden — never bare http.Error()
  • Responses must use httpresponse.OK, httpresponse.Created, httpresponse.NoContent with {data, meta} envelope
  • Routes mount under /api/preferences-api to match ingress routing
  • Database connection uses pkg/database with embedded SQL migrations
  • Auth middleware uses pkg/auth with JWT validation
  • The existing example CRUD code should be replaced, not left alongside preference code
  • user_id is a UUID, validated at the handler layer

Dependencies

  • PostgreSQL database available (connection via DATABASE_URL environment variable)
  • pkg/database package for connection pooling and migration support
  • pkg/auth package for JWT middleware and user extraction
  • JWT secret configured via JWT_SECRET environment variable
  • AUTH_ENABLED=true in production environments

Out of Scope

  • User registration or authentication (handled by a separate auth service)
  • Preference inheritance or defaults cascade (e.g., org-level defaults)
  • Real-time preference sync via WebSockets
  • Preference history or audit trail
  • Bulk preference operations across multiple users
  • Frontend UI for preference management
  • Rate limiting on preference endpoints
  • Preference schema versioning or migration

Open Questions

  1. Default values: Should GET return system defaults for keys that haven't been explicitly set (e.g., {"theme": "system", "language": "en", "notifications_enabled": "true"}) or only return keys the user has explicitly set?
  2. DELETE support: Should there be a DELETE /preferences/{user_id} to reset all preferences, or a DELETE /preferences/{user_id}/{key} to reset individual ones?
  3. Extensibility: When new preference keys are added in the future, should they be added to a configuration file/constant list, or is a code change acceptable?
  4. Admin access: Should admin users be allowed to read/write preferences for other users, or is self-access the only mode?