Skip to content

Settings Module

The Settings module is the per-user key/value preference store. It exposes two parallel access patterns over a single backing store: a whole-object view (fetch/replace the entire UserSettings document) and a per-key view (atomic individual reads/writes, batch sets, prefix queries). The web client uses the whole-object view for the Settings page and the per-key view for incremental UI state (active theme, sidebar collapsed, etc.).

Class Diagram

uses

uses

caches

queries

Controller

-service: Service

-logger: Logger

+GetSettings(w, r)

+UpdateSettings(w, r)

+ResetSettings(w, r)

+GetAll(w, r)

+Get(w, r)

+Set(w, r)

+SetBatch(w, r)

+Delete(w, r)

+Reset(w, r)

Service

-repo: Repository

-cache: Cache

-logger: Logger

+GetSettings()

+UpdateSettings()

+ResetSettings()

+Get()

+GetAll()

Repository

-db: Database

+Query()

+Insert()

+Update()

+Delete()

Cache

Database

Request Flow

  1. HTTP Request → Controller receives whole-settings or single-key request.
  2. Validation → validator.go enforces key naming rules and value-type constraints before the Service is invoked.
  3. Cache Check → Service consults Redis; misses fall through to the Repository.
  4. Persistence → Repository writes the settings row; Service invalidates the cache key on write.
  5. Response → Updated settings (or the single value) are returned.

Key Components

Controller

Two parallel surfaces:

  • Whole-object (GetSettings/UpdateSettings/ResetSettings) — operates on the entire UserSettings document.
  • Per-key (Get/Set/Delete + GetAll/SetBatch/Reset) — fine-grained access for clients that only need a slice.

Service

  • GetByPrefix lets feature-flag-style consumers query a namespaced subset (e.g. theme.*).
  • All writes invalidate the cached UserSettings blob to keep the two access surfaces consistent.

Repository

Stores settings as a JSON column under a single user_settings row per user — single-row updates avoid N writes for batch sets.

Validator

Centralizes key/value rules so the Service stays focused on persistence and caching.

Special Behaviors

Single-row JSON storage

All a user’s settings live in one row in user_settings with the values stored as a JSON column. That single-row choice means:

  • A whole-object update is one SQL UPDATE regardless of payload size.
  • A batch-of-N set is also one UPDATE (server-side merge).
  • The cached form is a single Redis key; invalidations are simple.

The trade-off: there’s no per-key Postgres-level index. Prefix queries are filtered in the Service after the row is loaded — fine for the < 100-key universe each user typically has, but it would need rethinking at much larger scale.

Validator runs at the controller boundary

validator.go is invoked in the Controller before the Service is touched. That keeps key naming rules (allowed characters, max length, namespace prefixes) and value-type constraints (e.g. boolean keys can’t accept arbitrary strings) one layer away from the Service’s persistence + caching focus.

Cache invalidation on every write

Both surfaces invalidate the cached UserSettings JSON on write. That means a per-key Set and a whole-object UpdateSettings are observationally equivalent — the next read sees the same merged object regardless of which write surface was used. No drift between the two views.

SDK Surface

  • SettingsService — both surfaces: getSettings/updateSettings/resetSettings (whole-object) and get/getAll/set/setBatch/delete/reset (per-key).
  • UserSettings — the structured shape (general / privacy / notifications / network sections).
  • actions/settings — undo-aware actions for incremental UI state changes.

Cross-Module Integration

ModuleRelationship
UsersOne row per user; deletes cascade.
ThemesActive theme id stored under theme.activeId.

API Reference

See the interactive API reference /settings tag for live examples.