ADR-005: Unify Settings Proxy
Status: Implemented Date: 2026-05-20 Updated: 2026-07-14 Deciders: (to be assigned)
Context
django-micboard previously had two parallel settings resolution mechanisms with overlapping concerns:
- The removed runtime settings proxy read from the host
MICBOARD_CONFIGdictionary and exposed overlapping convenience properties. - The former shared settings registry resolved DB-backed
Settingvalues but also exposed writes, bulk reads, and cache controls as a second public API.
Additionally, ~20 files accessed settings.MICBOARD_CONFIG directly rather than through
either service, scattering the reading surface across management commands, app startup, HTTP
clients, and maintenance services.
The duplicate APIs caused:
- Inconsistent attribute-style and
.get(key)access patterns. - Confusion about which API to use for a feature flag versus a scoped setting.
- Difficulty auditing where a setting was consumed.
- No single module whose deletion test exposed bypassing callers.
Decision
-
Consolidate into a single
SettingsServiceundermicboard/services/settings/settings_service.pywith a single public seam:SettingsService.get(key,default=None,*,organization=None,site=None,manufacturer=None,) -> AnyThe optional scope arguments select organization-, site-, or manufacturer-specific database values. Each definition selects one exact database scope; settings never fall through from one tenant scope to another. Immutable deployment controls (
MICBOARD_*) resolve from Django host settings first. Other keys resolve through the exact stored value, host configuration dictionary, app defaults, definition defaults, and the caller’s explicit default. -
Former proxy feature flags become registered keys in the unified service. Callers use
settings.get("msp_enabled")or the corresponding convenience property. -
All direct host configuration reads outside
SettingsServiceare replaced withSettingsService.get()orSettingsService.get_config_dict(). -
Remove the obsolete settings proxy without a compatibility layer. All callers import the public singleton or service class from its defining module,
micboard.services.settings.settings_service. -
Keep the registry private to the settings domain.
micboard/services/settings/registry.pyimplements scoped lookup and cache mechanics. Runtime callers do not import it. Authorized forms and admin workflows write throughSettingsPersistenceService;SettingsServiceowns public cache invalidation. -
Enforcement: An AST architecture test forbids direct
MICBOARD_*reads outside the unified service.
Consequences
- Positive: Single entry point for all configuration reads — one interface to test, one file to audit. Scope semantics and deployment-control precedence are centralized.
- Negative: Existing callers must move directly to the canonical service API.
- Migration: Implement the unified service, migrate every caller, and delete the obsolete proxy rather than retaining a compatibility shim.
Compliance
- CI enforces no direct
MICBOARD_*attribute or literalgetattrreads outside the settings service. - Runtime code does not import
SettingsRegistry; only settings-domain implementation tests may exercise it directly.
