Config Service — Domain Model
Status: populated Owner: TBD Last updated: 2026-04-18 Companion: Service Template · 03 platform-services · 02 DDD
1. Aggregates
1.1 ConfigNode
The universal node in the configuration DAG. All configurable entities are ConfigNode instances.
| Field | Type | Invariants |
|---|---|---|
id | ConfigNodeId (prefix cfgn_) | Immutable after creation |
tenantId | TenantId | null | null = global node; all non-global nodes must be tenant-scoped |
nodeType | ConfigNodeType | Must be one of 13 valid types (see §4) |
nodeKey | string | Stable within scope; unique per (tenantId, nodeType, nodeKey) |
parentId | ConfigNodeId | null | Parent must exist; parent type must be valid per taxonomy |
payload | JSONB | Type-specific; validated against node-type schema on write |
scopeChain | ConfigNodeId[] | Denormalised ancestor array; updated on parentId change |
isActive | boolean | Soft-disable only; hard-delete not supported |
version | number | Optimistic lock |
createdAt | Timestamp | — |
updatedAt | Timestamp | — |
State machine:
Invariants:
parentIdedge that would form a cycle →CONFIG_CIRCULAR_REFERENCE.- Soft-deleting a node with active children →
CONFIG_NODE_HAS_CHILDREN. GLOBALnode andisSystem=trueroles: creatable/mutable bySUPER_ADMINonly.
1.2 FeatureDefinition
Defines a named capability within a module, its permitted actions, and data scope.
| Field | Type | Notes |
|---|---|---|
id | FeatureId (prefix feat_) | — |
tenantId | TenantId | — |
featureKey | string | Stable code, e.g. ViewMedications |
moduleKey | string | Parent module key, e.g. CLIN-MEDS |
allowedActions | string[] | Actions this feature may grant |
dataScopeType | DataScopeType | world | tenant | networkOnly | facilityOnly | sameFacility | self |
isActive | boolean | — |
1.3 RoleDefinition
A named capability group; supports inheritance.
| Field | Type | Notes |
|---|---|---|
id | RoleId (prefix role_) | — |
tenantId | TenantId | null | null = system/platform role |
roleKey | string | Stable code, e.g. PHYSICIAN, NURSE |
displayName | string | Human-readable |
isAbstract | boolean | Cannot be directly assigned to users |
isSystem | boolean | Only SUPER_ADMIN can create/modify |
1.4 RoleInheritance
A DAG edge from child role to parent role.
| Field | Type | Notes |
|---|---|---|
id | RoleInheritanceId (prefix ri_) | — |
tenantId | TenantId | — |
childRoleId | RoleId | Role that inherits |
parentRoleId | RoleId | Role being inherited from |
inheritanceType | full | partial | full = all grants propagate |
Invariant: Edge creating a cycle → CIRCULAR_ROLE_INHERITANCE. Max depth 10 enforced at definition time.
1.5 RoleFeatureGrant
Explicit grant or deny of a feature + action set for a role.
| Field | Type | Notes |
|---|---|---|
id | GrantId (prefix grant_) | — |
tenantId | TenantId | — |
roleId | RoleId | — |
featureKey | string | — |
grantedActions | string[] | Explicit allows |
deniedActions | string[] | Explicit denies; override inherited grants |
1.6 UserNodeOverride
Explicit per-user per-node allow/deny; highest resolution priority.
| Field | Type | Notes |
|---|---|---|
id | OverrideId (prefix ovr_) | — |
tenantId | TenantId | — |
userId | UserId | Must belong to same tenant as caller |
nodeId | ConfigNodeId | Hierarchy node where override applies |
featureKey | string | — |
action | string | The specific action |
effect | allow | deny | — |
justification | string | Mandatory non-empty |
effectiveFrom | Date | Required |
effectiveTo | Date | null | Optional; expired = silently ignored |
grantedBy | UserId | Creating actor |
Invariants:
- One active override per
(userId, nodeId, featureKey, action)→ conflict:OVERRIDE_CONFLICT. ExplicitDenyis final; no role grant or ABAC policy can override it.ExplicitAllowcascades to UI element visibility for the bound action at that node.
1.7 UIDefinition
A UI element and its relationship to features and actions.
| Field | Type | Notes |
|---|---|---|
id | UIDefId (prefix uid_) | — |
tenantId | TenantId | — |
elementKey | string | Stable key, e.g. add-medication-btn |
elementType | screen | component | element | action_binding | — |
parentElementId | UIDefId | null | Hierarchy: screen → component → element |
featureKey | string | Feature this element belongs to |
actionBinding | string | null | Action triggered, e.g. medication:write |
defaultProps | { visible: boolean, interactable: boolean } | — |
1.8 UIVisibilityRule
Per-role or per-user visibility override for a UI element.
| Field | Type | Notes |
|---|---|---|
id | UIRuleId (prefix uir_) | — |
tenantId | TenantId | — |
elementId | UIDefId | FK → UIDefinition |
subjectType | role | user | — |
subjectId | RoleId | UserId | — |
isVisible | boolean | — |
isInteractable | boolean | — |
nodeId | ConfigNodeId | null | If set, rule only applies at this node scope |
Invariant: User rule always overrides conflicting role rule for same element + node.
1.9 DesignToken
A scoped design-system token value.
| Field | Type | Notes |
|---|---|---|
id | TokenId (prefix tok_) | — |
tenantId | TenantId | — |
scopeNodeId | ConfigNodeId | Config node where this token is defined |
scopeType | global | tenant | module | user | — |
tokenKey | string | e.g. brand.primary, font.family.rtl |
tokenValue | string | e.g. #006600 |
locale | string | null | e.g. ps-AF; overrides non-locale token for same key |
Merge priority (LWW per tier): User > Module > OrgNode (deepest wins) > Tenant > Global
2. Value Objects
| Value Object | Type / Notes |
|---|---|
ConfigNodeId | Branded ULID, prefix cfgn_ |
FeatureId | Branded ULID, prefix feat_ |
RoleId | Branded ULID, prefix role_ |
OverrideId | Branded ULID, prefix ovr_ |
UIDefId | Branded ULID, prefix uid_ |
TokenId | Branded ULID, prefix tok_ |
ConfigNodeType | 13-value enum (see §4) |
DataScopeType | world | tenant | networkOnly | facilityOnly | sameFacility | self |
EvaluationResult | { effect, reason, policyId?, dataScope?, uiConfig?, designTokens? } |
UIElementConfig | { elementKey, elementType, visible, interactable, actionBinding, children[] } |
3. Domain Events
| Event subject (NATS) | Trigger | Retention |
|---|---|---|
config.feature.created.v1 | Feature definition created | 7 years |
config.feature.updated.v1 | Feature definition updated | 7 years |
config.role.created.v1 | Role definition created | 7 years |
config.role.updated.v1 | Role definition updated | 7 years |
config.role.deleted.v1 | Role soft-deleted | 7 years |
config.role_grant.created.v1 | Role feature grant created | 7 years |
config.role_grant.updated.v1 | Role feature grant updated | 7 years |
config.ui_definition.created.v1 | UI element created | 7 years |
config.ui_definition.updated.v1 | UI element updated | 7 years |
config.user_override.created.v1 | User node override created | 7 years |
config.user_override.deleted.v1 | User node override soft-deleted | 7 years |
config.design_token.updated.v1 | Design token value changed | 7 years |
config.config.cache_busted.v1 | Cache eviction for a tenant/scope | 7 days |
4. DAG Node Taxonomy
node_type | Description | Valid Parent Types |
|---|---|---|
GLOBAL | Platform-wide defaults | — (root) |
TENANT | Tenant root config | GLOBAL |
ORG_NODE | Maps 1:1 to facility/hierarchy node | TENANT, ORG_NODE |
MODULE | Module catalogue key scope | TENANT, ORG_NODE |
FEATURE | Feature within a module | MODULE |
ACTION | Allowed action within a feature | FEATURE |
ROLE | Role definition | TENANT |
USER | User-level explicit override scope | TENANT |
UI_SCREEN | Screen definition | FEATURE |
UI_COMPONENT | Component within a screen | UI_SCREEN |
UI_ELEMENT | Element within a component | UI_COMPONENT |
ACTION_BINDING | Element → Action link | UI_ELEMENT |
DESIGN_SYSTEM | Design token scope node | GLOBAL, TENANT, MODULE, USER |
5. Ubiquitous Language
| Term | Definition |
|---|---|
| ConfigNode | Any node in the configuration DAG |
| Config Spine | The ancestor chain of hierarchy nodes used as resolution context |
| EvaluationResult | Final output of the 9-step resolution pipeline |
| ExplicitAllow | UserNodeOverride with effect = "allow" — highest priority |
| ExplicitDeny | UserNodeOverride with effect = "deny" — final, cannot be overridden |
| Role Graph | BFS-expanded DAG of role inheritance edges |
| DataScope | Determines which data records a granted action may touch |
| Design Token | Key-value pair defining a design-system variable |
| ActionBinding | Link from a UI element to the action it triggers |
| Scope Chain | Denormalised ancestor ID array on each ConfigNode |
| Module License | License record (owned by platform-admin-service) gating module access |
| Feature Flag | Platform-level toggle per tenant (owned by platform-admin-service) |