Skip to main content

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.

FieldTypeInvariants
idConfigNodeId (prefix cfgn_)Immutable after creation
tenantIdTenantId | nullnull = global node; all non-global nodes must be tenant-scoped
nodeTypeConfigNodeTypeMust be one of 13 valid types (see §4)
nodeKeystringStable within scope; unique per (tenantId, nodeType, nodeKey)
parentIdConfigNodeId | nullParent must exist; parent type must be valid per taxonomy
payloadJSONBType-specific; validated against node-type schema on write
scopeChainConfigNodeId[]Denormalised ancestor array; updated on parentId change
isActivebooleanSoft-disable only; hard-delete not supported
versionnumberOptimistic lock
createdAtTimestamp—
updatedAtTimestamp—

State machine:

Invariants:

  • parentId edge that would form a cycle → CONFIG_CIRCULAR_REFERENCE.
  • Soft-deleting a node with active children → CONFIG_NODE_HAS_CHILDREN.
  • GLOBAL node and isSystem=true roles: creatable/mutable by SUPER_ADMIN only.

1.2 FeatureDefinition​

Defines a named capability within a module, its permitted actions, and data scope.

FieldTypeNotes
idFeatureId (prefix feat_)—
tenantIdTenantId—
featureKeystringStable code, e.g. ViewMedications
moduleKeystringParent module key, e.g. CLIN-MEDS
allowedActionsstring[]Actions this feature may grant
dataScopeTypeDataScopeTypeworld | tenant | networkOnly | facilityOnly | sameFacility | self
isActiveboolean—

1.3 RoleDefinition​

A named capability group; supports inheritance.

FieldTypeNotes
idRoleId (prefix role_)—
tenantIdTenantId | nullnull = system/platform role
roleKeystringStable code, e.g. PHYSICIAN, NURSE
displayNamestringHuman-readable
isAbstractbooleanCannot be directly assigned to users
isSystembooleanOnly SUPER_ADMIN can create/modify

1.4 RoleInheritance​

A DAG edge from child role to parent role.

FieldTypeNotes
idRoleInheritanceId (prefix ri_)—
tenantIdTenantId—
childRoleIdRoleIdRole that inherits
parentRoleIdRoleIdRole being inherited from
inheritanceTypefull | partialfull = 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.

FieldTypeNotes
idGrantId (prefix grant_)—
tenantIdTenantId—
roleIdRoleId—
featureKeystring—
grantedActionsstring[]Explicit allows
deniedActionsstring[]Explicit denies; override inherited grants

1.6 UserNodeOverride​

Explicit per-user per-node allow/deny; highest resolution priority.

FieldTypeNotes
idOverrideId (prefix ovr_)—
tenantIdTenantId—
userIdUserIdMust belong to same tenant as caller
nodeIdConfigNodeIdHierarchy node where override applies
featureKeystring—
actionstringThe specific action
effectallow | deny—
justificationstringMandatory non-empty
effectiveFromDateRequired
effectiveToDate | nullOptional; expired = silently ignored
grantedByUserIdCreating actor

Invariants:

  • One active override per (userId, nodeId, featureKey, action) → conflict: OVERRIDE_CONFLICT.
  • ExplicitDeny is final; no role grant or ABAC policy can override it.
  • ExplicitAllow cascades to UI element visibility for the bound action at that node.

1.7 UIDefinition​

A UI element and its relationship to features and actions.

FieldTypeNotes
idUIDefId (prefix uid_)—
tenantIdTenantId—
elementKeystringStable key, e.g. add-medication-btn
elementTypescreen | component | element | action_binding—
parentElementIdUIDefId | nullHierarchy: screen → component → element
featureKeystringFeature this element belongs to
actionBindingstring | nullAction triggered, e.g. medication:write
defaultProps{ visible: boolean, interactable: boolean }—

1.8 UIVisibilityRule​

Per-role or per-user visibility override for a UI element.

FieldTypeNotes
idUIRuleId (prefix uir_)—
tenantIdTenantId—
elementIdUIDefIdFK → UIDefinition
subjectTyperole | user—
subjectIdRoleId | UserId—
isVisibleboolean—
isInteractableboolean—
nodeIdConfigNodeId | nullIf 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.

FieldTypeNotes
idTokenId (prefix tok_)—
tenantIdTenantId—
scopeNodeIdConfigNodeIdConfig node where this token is defined
scopeTypeglobal | tenant | module | user—
tokenKeystringe.g. brand.primary, font.family.rtl
tokenValuestringe.g. #006600
localestring | nulle.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 ObjectType / Notes
ConfigNodeIdBranded ULID, prefix cfgn_
FeatureIdBranded ULID, prefix feat_
RoleIdBranded ULID, prefix role_
OverrideIdBranded ULID, prefix ovr_
UIDefIdBranded ULID, prefix uid_
TokenIdBranded ULID, prefix tok_
ConfigNodeType13-value enum (see §4)
DataScopeTypeworld | 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)TriggerRetention
config.feature.created.v1Feature definition created7 years
config.feature.updated.v1Feature definition updated7 years
config.role.created.v1Role definition created7 years
config.role.updated.v1Role definition updated7 years
config.role.deleted.v1Role soft-deleted7 years
config.role_grant.created.v1Role feature grant created7 years
config.role_grant.updated.v1Role feature grant updated7 years
config.ui_definition.created.v1UI element created7 years
config.ui_definition.updated.v1UI element updated7 years
config.user_override.created.v1User node override created7 years
config.user_override.deleted.v1User node override soft-deleted7 years
config.design_token.updated.v1Design token value changed7 years
config.config.cache_busted.v1Cache eviction for a tenant/scope7 days

4. DAG Node Taxonomy​

node_typeDescriptionValid Parent Types
GLOBALPlatform-wide defaults— (root)
TENANTTenant root configGLOBAL
ORG_NODEMaps 1:1 to facility/hierarchy nodeTENANT, ORG_NODE
MODULEModule catalogue key scopeTENANT, ORG_NODE
FEATUREFeature within a moduleMODULE
ACTIONAllowed action within a featureFEATURE
ROLERole definitionTENANT
USERUser-level explicit override scopeTENANT
UI_SCREENScreen definitionFEATURE
UI_COMPONENTComponent within a screenUI_SCREEN
UI_ELEMENTElement within a componentUI_COMPONENT
ACTION_BINDINGElement → Action linkUI_ELEMENT
DESIGN_SYSTEMDesign token scope nodeGLOBAL, TENANT, MODULE, USER

5. Ubiquitous Language​

TermDefinition
ConfigNodeAny node in the configuration DAG
Config SpineThe ancestor chain of hierarchy nodes used as resolution context
EvaluationResultFinal output of the 9-step resolution pipeline
ExplicitAllowUserNodeOverride with effect = "allow" — highest priority
ExplicitDenyUserNodeOverride with effect = "deny" — final, cannot be overridden
Role GraphBFS-expanded DAG of role inheritance edges
DataScopeDetermines which data records a granted action may touch
Design TokenKey-value pair defining a design-system variable
ActionBindingLink from a UI element to the action it triggers
Scope ChainDenormalised ancestor ID array on each ConfigNode
Module LicenseLicense record (owned by platform-admin-service) gating module access
Feature FlagPlatform-level toggle per tenant (owned by platform-admin-service)