UMS Parameterization System Specification
Version:1.0.0
Status:Proposed
Created:2026-05-30
Last Updated:2026-05-30
1. Overview
1.1 Purpose
Establish a mandatory pattern for all configurable behaviors in the UMS system. No business rules, validations, limits, durations, behaviors, permissions, or operational configurations may remain hardcoded in web, API, services, or internal components.
1.2 Scope
All configurable behaviors within UMS must be stored in the database, loaded into memory at system startup, and consumed through a centralized configuration provider service.
2. Parameterization Architecture
2.1 Parameter Storage Zones
┌─────────────────────────────────────────────────────────────┐
│ UMS Configuration │
├─────────────────────────┬───────────────────────────────────┤
│ GLOBAL (System-wide) │ TENANT-SPECIFIC │
│ ScopeId = 1 │ ScopeId = 2 │
├─────────────────────────┼───────────────────────────────────┤
│ • Visible to Internal │ • Visible to Tenant Admin │
│ Admin only │ (with permissions) │
│ • Applies to entire │ • Applies only to own tenant │
│ UMS system │ • Internal Admin can view all │
│ • Base configuration │ • Tenant can override if global │
│ for all behaviors │ allows it │
└─────────────────────────┴───────────────────────────────────┘
2.2 Scope Identifiers
| ScopeId |
Name |
Description |
|
| 1 |
Global |
System-wide parameters, UMS base configuration |
|
| 2 |
Tenant |
Tenant-specific parameters |
|
| 3 |
Suite |
System Suite level parameters (future) |
|
| 4 |
Module |
Module level parameters (future) |
— |
3. Parameter Loader
3.1 Purpose
Load all parameters from the database into memory at system startup to avoid repeated database queries during runtime.
3.2 Behavior
System Startup
│
▼
┌────────────────────┐
│ ConfigurationLoader│
│ │
│ 1. Load Global │ ← ScopeId = 1
│ Parameters │
│ │
│ 2. Load Tenant │ ← For active tenants
│ Parameters │
│ │
│ 3. Build in-memory │
│ parameter store │
│ │
│ 4. Register as │
│ singleton │
└────────────────────┘
│
▼
┌────────────────────┐
│ConfigurationProvider│ ← Consumed by all system logic
│ │
│ get(key) │
│ get(key, tenantId) │
│ set(key, value) │ ← Triggers audit log
└────────────────────┘
3.3 Loading Strategy
- Global Parameters: Loaded synchronously at startup
- Tenant Parameters: Loaded on-demand when tenant context is established, then cached
- Cache Refresh: Manual trigger only (no automatic refresh during runtime)
3.4 Memory Structure
interface ParameterStore {
global: Map<string, AppConfiguration>;
byTenant: Map<Guid, Map<string, AppConfiguration>>;
}
4. Parameter Domain Model
The configuration system is modeled as three Aggregate Roots so that schema, default value, and tenant override evolve independently while still following the mandatory code, value, description contract.
| Aggregate Root |
Role |
|
ParameterDefinition |
Defines the schema and business meaning of a configurable parameter |
|
ParameterGlobalValue |
Stores the default system-wide value for the parameter |
|
ParameterTenantValue |
Stores the tenant-specific override when policy allows it |
### Shared Contract |
code identifies the parameter.
value stores the current effective content.
description explains purpose, scope, and business impact.
ParameterTenantValue must always reference a ParameterDefinition.
ParameterGlobalValue acts as the baseline when no tenant override exists.
5. Configuration Provider Service
4.1 Interface
interface IConfigurationProvider {
// Get global parameter
getGlobal(key: string): AppConfiguration | undefined;
// Get tenant parameter (with precedence check)
getForTenant(tenantId: Guid, key: string): AppConfiguration | undefined;
// Get parameter value as typed value
getValueAs<T>(key: string, tenantId?: Guid, defaultValue?: T): T;
// Set parameter (triggers audit)
set(key: string, value: string, scope: Scope, tenantId?: Guid): void;
// Reload from database
reload(): Promise<void>;
// Reload for specific tenant
reloadTenant(tenantId: Guid): Promise<void>;
}
4.2 Precedence Rules
- If tenant-specific parameter exists → Use tenant value
- If only global parameter exists → Use global value
- Global parameters act as maximum restrictionwhen applicable
- Tenant cannot overridewhen global parameter is marked as non-overridable
function getWithPrecedence(key: string, tenantId?: Guid): AppConfiguration | undefined {
if (tenantId) {
const tenantParam = store.byTenant.get(tenantId)?.get(key);
if (tenantParam) return tenantParam;
}
return store.global.get(key);
}
6. Parameter Structure
5.1 AppConfiguration Entity
| Field |
Type |
Description |
|
| Id |
Guid |
Unique identifier |
|
| TenantId |
Guid? |
Tenant owner (null for Global) |
|
| SystemSuiteId |
Guid? |
System Suite (for Suite-level) |
|
| ModuleId |
Guid? |
Module (for Module-level) |
|
| Code |
string |
Parameter code (unique within scope) |
|
| Value |
string |
Parameter value |
|
| Description |
string |
Human-readable description |
|
| ScopeId |
int |
1=Global, 2=Tenant, 3=Suite, 4=Module |
|
| IsInheritable |
bool |
Can be inherited by child scopes |
|
| IsEncrypted |
bool |
Value is encrypted |
|
| Version |
string |
Semantic version |
|
| StatusId |
int |
1=Draft, 2=Published, 3=Archived |
### 5.2 Required Parameters (Initial Set) |
Global Parameters (ScopeId = 1)
| Code |
Type |
Default |
Description |
|
| SESSION_TIMEOUT_MINUTES |
int |
30 |
Idle session timeout |
|
| MAX_LOGIN_ATTEMPTS |
int |
5 |
Max failed login attempts |
|
| ACCESS_TOKEN_DURATION_MS |
int |
3600000 |
Access token lifetime (1 hour) |
|
| REFRESH_TOKEN_DURATION_MS |
int |
604800000 |
Refresh token lifetime (7 days) |
|
| MIN_PASSWORD_LENGTH |
int |
12 |
Minimum password length |
|
| MAX_VALIDITY_PERIOD_DAYS |
int |
365 |
Account validity period |
|
| MFA_REQUIRED_FOR_ADMIN |
bool |
false |
Tenant-scoped toggle that requires verified MFA at login when enabled |
|
| PASSWORD_HISTORY_COUNT |
int |
5 |
Password history size |
|
| UI_LANGUAGE_DEFAULT |
string |
“es” |
Default UI language |
|
| UI_TIMEZONE_DEFAULT |
string |
“America/Lima” |
Default timezone |
|
| EMAIL_FROM_ADDRESS |
string |
“noreply@ums.local” |
Email sender |
|
| NOTIFICATION_RETRY_ATTEMPTS |
int |
3 |
Notification retry count |
#### Tenant-Specific Parameters (ScopeId = 2) |
| Code |
Type |
Description |
| MFA_ALLOWED_METHODS |
string[] |
Allowed MFA methods (SMS, TOTP, Email, WebAuthn) |
| PASSWORD_MAX_AGE_DAYS |
int |
Password max age (overrides global if set) |
| ACCOUNT_LOCKOUT_DURATION_MINUTES |
int |
Account lockout duration |
| UI_CUSTOM_BRANDING_ENABLED |
bool |
Enable custom branding |
| SESSION_MAX_CONCURRENT |
int |
Max concurrent sessions per user |
Tenant login responses and the effective auth graph must resolve these values per tenant so the portal can enforce the same MFA policy without assuming it is global.
7. Audit Logging
6.1 Audited Operations
- Create parameter
- Update parameter value
- Update parameter status (Draft → Published → Archived)
- Delete parameter
- Override global parameter at tenant level
6.2 Audit Record Structure
interface ConfigurationAuditRecord {
id: Guid;
parameterId: Guid;
parameterCode: string;
tenantId: Guid | null; // null for global parameters
userId: Guid;
userType: 'InternalAdmin' | 'TenantAdmin' | 'System';
operation: 'Create' | 'Update' | 'Delete' | 'Override';
previousValue: string | null;
newValue: string | null;
timestamp: DateTime;
ipAddress: string;
userAgent: string;
}
8. API Endpoints
8.1 Global Parameters (Internal Admin Only)
| Method |
Endpoint |
Description |
|
| GET |
/api/v1/configurations/global |
List all global parameters |
|
| GET |
/api/v1/configurations/global/{code} |
Get global parameter by code |
|
| POST |
/api/v1/configurations/global |
Create global parameter |
|
| PUT |
/api/v1/configurations/global/{code} |
Update global parameter |
|
| DELETE |
/api/v1/configurations/global/{code} |
Archive global parameter |
### 7.2 Tenant Parameters |
| Method |
Endpoint |
Description |
|
| GET |
/api/v1/tenants/{tenantId}/configurations |
List tenant parameters |
|
| GET |
/api/v1/tenants/{tenantId}/configurations/{code} |
Get tenant parameter |
|
| POST |
/api/v1/tenants/{tenantId}/configurations |
Create tenant parameter |
|
| PUT |
/api/v1/tenants/{tenantId}/configurations/{code} |
Update tenant parameter |
|
| DELETE |
/api/v1/tenants/{tenantId}/configurations/{code} |
Archive tenant parameter |
— |
8. Frontend Integration
8.1 Configuration Screen Structure
┌─────────────────────────────────────────────────────────────┐
│ Parámetros del Sistema │
├─────────────────────────┬───────────────────────────────────┤
│ [Global] [Por Tenant] │ │
├─────────────────────────┤ ┌─────────────────────────────┐ │
│ Search... │ │ SESSION_TIMEOUT_MINUTES │ │
│ │ │ Valor: 30 │ │
│ [Global] │ │ Descripción: Idle session │ │
│ • SESSION_TIMEOUT... │ │ timeout in minutes │ │
│ • MAX_LOGIN_ATTEMPTS │ │ Scope: Global │ │
│ • ACCESS_TOKEN_... │ │ Status: Published │ │
│ │ │ │ │
│ [Por Tenant] │ │ [Edit] [Archive] │ │
│ ▼ TENANT: Ransa │ └─────────────────────────────┘ │
│ • MFA_ALLOWED_... │ │
│ • PASSWORD_MAX_... │ │
│ ▼ TENANT: APM │ │
│ • MFA_ALLOWED_... │ │
└─────────────────────────┴───────────────────────────────────┘
8.2 Tenant Configuration Tab
┌─────────────────────────────────────────────────────────────┐
│ Tenant: Ransa S.A. [Parámetros] │
├─────────────────────────────────────────────────────────────┤
│ │
│ Configuraciones específicas para este tenant │
│ │
│ [+ Agregar Parámetro] │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ MFA_ALLOWED_METHODS │ │
│ │ Valor: ["TOTP", "Email"] │ │
│ │ Override del global: No establecido │ │
│ │ [Edit] [Remove Override] │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
9. Technical Implementation
9.1 Backend Services
- ConfigurationLoader (Singleton)
- Loads parameters at startup
- Manages in-memory cache
- Handles reload requests
- ConfigurationProvider (Singleton)
- Central access point for all configuration values
- Implements precedence logic
- Notifies audit service on changes
- ConfigurationAuditService
- Logs all configuration changes
- Queries audit records
- ConfigurationValues
- Typed facade over
IConfigurationProvider for login, session, and password consumers
- Keeps configuration lookups centralized in handlers and validators
9.2 TODO: Future Redis Migration
/**
* TODO: [TD-003] Migrate ConfigurationProvider to Redis
*
* Current implementation stores parameters in-memory.
* Future implementation should use Redis for distributed caching.
*
* Migration steps:
* 1. Introduce IConfigurationCache abstraction
* 2. Implement InMemoryConfigurationCache (current)
* 3. Implement RedisConfigurationCache (future)
* 4. Update ConfigurationProvider to use IConfigurationCache
* 5. Add cache invalidation strategy
* 6. Implement cache warming on startup
*
* Benefits:
* - Shared configuration across API instances
* - Automatic invalidation on updates
* - Reduced database load
* - Support for configuration hot-reload
*/
10. Acceptance Criteria
10.1 Functional Requirements
10.2 Technical Requirements
10.3 Security Requirements
12. Changelog
| Version |
Date |
Changes |
| 1.0.0 |
2026-05-30 |
Initial specification |