Estado: Aceptado Fecha: 2026-05-31 Responsable de Decisión: Arquitectura Relacionados:
ADR-0073 establece el JSON Schema del AuthorizationGraph como el contrato canónico entre el servidor UMS y tres runtimes de SDK (.NET, TypeScript, NestJS). El schema evoluciona: se agregan campos nuevos cuando el grafo carga información nueva, campos existentes se deprecan, ocasionalmente un campo se remueve o su semántica cambia.
Un SDK multi-runtime con cadencia de release independiente por paquete significa que el servidor puede emitir un grafo en versión N mientras un cliente todavía corre un SDK que conoce la versión N-1. Sin una política de compatibilidad documentada, cada cambio de schema se convierte en un problema de coordinación que puede romper clientes en producción.
Este ADR define cómo se versiona el schema, cómo se decide la compatibilidad por categoría de cambio, y cómo los SDKs y el servidor lo enforce.
schemaVersionCada payload AuthorizationGraph emitido por AuthorizationGraphBuilderService incluye un string top-level schemaVersion conforme a versionado semántico (MAJOR.MINOR.PATCH).
{
"schemaVersion": "1.2.0",
"context": { ... },
...
}
El campo es requerido y validado contra la constante schemaVersion del propio auth-graph.schema.json. La ausencia del campo es interpretada por los SDKs como “v0.x — no soportado, rechazar deserialización”.
| Bump | Disparador |
|---|---|
| MAJOR | Un cambio que un SDK targeting el major previo no puede deserializar o interpretar de forma segura. Ejemplos: remover un campo requerido, renombrar un campo, narrowing del tipo de un campo, remover un valor de enum que puede aparecer en payloads viejos, cambiar la semántica de resolución (ej. invertir precedencia Allow/Deny). |
| MINOR | Un cambio que es backward-compatible para lectores del major previo: agregar un campo opcional, agregar un valor nuevo a una enumeración abierta, agregar una sección nueva independiente de secciones existentes, agregar un nuevo tipo de criteria en featureFlags[]. |
| PATCH | Cambios solo de documentación al schema, refinamiento de patrones de validación que no rechazan payloads previamente válidos, clarificación de descripciones de campos. Sin cambios estructurales. |
Ante la duda, preferir el bump mayor. Falso-MAJOR es molesto; falso-MINOR es un incidente en producción.
Cada paquete SDK declara su rango de schema soportado usando sintaxis de range semver en su metadata de paquete:
<UmsSchemaCompatibility>>=1.0.0 <2.0.0</UmsSchemaCompatibility>"umsSchemaCompatibility": ">=1.0.0 <2.0.0"El SDK lo lee al inicio, lo expone vía ISdkMetadata.SchemaCompatibility (o equivalente en cada runtime), y lo usa para validar grafos entrantes.
| Servidor emite | SDK soporta | Comportamiento del SDK |
|---|---|---|
1.0.0 |
>=1.0.0 <2.0.0 |
Accept — match exacto |
1.2.0 |
>=1.0.0 <2.0.0 |
Accept con warning — el servidor es más nuevo, campos desconocidos preservados pero no usados. Logear evento estructurado SchemaMinorMismatchEvent. |
1.0.0 |
>=1.2.0 <2.0.0 |
Accept con warning — el SDK es más nuevo, el servidor emite subset. Logear evento estructurado SchemaServerOlderEvent. |
2.0.0 |
>=1.0.0 <2.0.0 |
Reject — error AUTH_GRAPH_SCHEMA_UNSUPPORTED. El SDK rechaza deserializar. |
0.x o ausente |
cualquiera | Reject — error AUTH_GRAPH_SCHEMA_MISSING. |
La justificación: un bump MINOR significa que lectores de la versión previa todavía pueden extraer todo lo que necesitan; el warning es apropiado. Un bump MAJOR significa que la semántica cambió; solo el rechazo duro es seguro.
Al deserializar un grafo en un MINOR adelantado al ceiling de compatibilidad del SDK, los campos desconocidos son:
extensions: Map<string, unknown>), para que re-serialización (para cache, logging) haga round-trip fielmente.UnknownFieldsObservedEvent listando los paths de campos, para que operadores detecten staleness del SDK.Esto rechaza explícitamente dos alternativas: strict-only (rechazar cualquier campo desconocido) que vuelve breaking los bumps MINOR, y silent-ignore que oculta staleness de operaciones.
Remover o cambiar un campo requiere la siguiente secuencia:
deprecated: true en el JSON Schema con description apuntando al reemplazo y una versión target de remoción.DeprecatedFieldUsageEvent cada vez que código consumidor lee el campo deprecado.Ventana mínima entre pasos 1 y 3: dos releases MINOR o seis meses, lo que sea mayor.
docs/sdk/contracts/compatibility-matrix.md (bilingüe) lista cada versión publicada de SDK contra cada versión emitida de schema, marcado como Accept, Accept-with-warning o Reject. CI actualiza este archivo cuando se liberan SDKs. La matriz es la fuente de verdad para preguntas de soporte (“¿funciona Ums.Sdk.Authorization 1.4.2 con UMS servidor emitiendo schema 2.0?”).
auth-graph.schema.json lleva schemaVersion: "1.0.0".Ums.Sdk.Contracts puede ser 1.0.0, 1.5.3, etc. — su versión refleja cambios de API en la superficie de DTO, que pueden bumpear independientemente (ej. para refactors de naming que no afectan el wire schema).Los dos ejes de versión nunca se confunden. El grafo lleva schemaVersion. El SDK declara umsSchemaCompatibility. Ninguno dice nada sobre la versión de paquete del otro.
El schema es un contrato público consumido por código que no controlamos (sistemas cliente de terceros corriendo SDKs desplegados). Semver es el vocabulario universalmente entendido para promesas de compatibilidad. Inventar un esquema custom significa que cada consumidor tiene que aprenderlo.
Esto coincide con la promesa de semver: bumps MAJOR explícitamente señalan “debes actualizar para seguir funcionando”, bumps MINOR explícitamente señalan “tu código sigue funcionando”. Rechazar duro los mismatches MAJOR fuerza upgrades que de otro modo serían inseguros; aceptar MINOR con logging surface staleness sin romper producción.
Una sola ventana MINOR es demasiado corta — los consumidores pueden ni notar la deprecación antes de que se vaya. Ventanas indefinidas saturan el schema. Dos MINORs / seis meses coincide con la cadencia a la que típicamente sistemas cliente refrescan sus dependencias de SDK en entornos regulados.
Stripear campos desconocidos rompe la serialización round-trip — si el SDK cachea el grafo y luego lo re-emite (para diagnostic dumps, debugging), el grafo cacheado difiere silenciosamente de lo que el servidor envió. Preservar con extensions mantiene integridad round-trip sin otorgarle peso de autorización a los campos desconocidos.
Ingenieros de soporte, partners de integración y revisores de seguridad preguntan “¿qué funciona con qué?” vía canales de documentación, no leyendo fixtures de test. Una matriz mantenida es la respuesta de menor fricción. El mantenimiento por CI previene drift de la realidad.
extensions para campos desconocidos agrega overhead de memoria por instancia de grafo (insignificante en la práctica pero presente).schemaVersion se vuelve un nuevo campo requerido pero es una sola adición de string — sin impacto estructural en consumidores existentes (después de un bump MAJOR a 1.0.0).| Componente | Ubicación | Owner |
|---|---|---|
auth-graph.schema.json con constante schemaVersion |
src/libs/sdk/contracts/ |
Arquitectura |
SCHEMA_VERSIONING.md resumen developer |
src/libs/sdk/contracts/ |
Arquitectura |
compatibility-matrix.md (EN + ES) |
docs/sdk/contracts/ |
Arquitectura + Release |
Servidor emite schemaVersion en el grafo |
Ums.Application/Authorization/Graph/AuthorizationGraphBuilderService.cs |
Backend |
Metadata del SDK declara umsSchemaCompatibility |
Cada paquete SDK | Equipo SDK |
| SDK rechaza mismatch MAJOR, advierte en MINOR | Ums.Sdk.Authorization, @ums/sdk-authorization |
Equipo SDK |
Eventos logeados: UnknownFieldsObservedEvent, SchemaMinorMismatchEvent, DeprecatedFieldUsageEvent |
Todos los SDKs | Equipo SDK |
| Job CI: round-trip de cada fixture por cada SDK | .github/workflows/sdk-contract-validation.yml |
Plataforma |
Release inicial: schema 1.0.0. La estructura actual del grafo documentada en auth-graph.md §8 es el schema canónico 1.0.0.