Skip to content

Latest commit

 

History

History
86 lines (77 loc) · 25.7 KB

File metadata and controls

86 lines (77 loc) · 25.7 KB
title Registro de Decisiones Arquitectónicas Locales
type hub
classification Product ADR
owner Evolith Tracker Team

DECISIONS.md — Evolith Tracker (ADR Hub)

Bilingual Navigation: English (this document) · Versión en Español

Propósito

Este documento sirve como el Hub de Decisiones Arquitectónicas Locales para Evolith Tracker. Documenta las decisiones y desviaciones específicas de este producto satélite frente al Core.

Nota: Las decisiones universales se heredan del Upstream Base (evolith_arch32).

  • Upstream base: https://github.com/beyondnetcode/evolith_arch32
  • Última clasificación: 2026-06-28
  • Convención de IDs de ADR upstream: calificados por categoría — core/, nodejs/, dotnet/, android/ (p. ej. core/0074, nodejs/0075).
  • ADRs locales (satélite): este repositorio es un satélite gobernado — ver evolith.yaml. Los ADRs locales conservan su namespace T-{NNN} (distinto del ADR-{NNNN} del Core, per T-011), siguen la plantilla canónica de 6 secciones del Core, llevan el tag EvolithSatellite en su frontmatter y están registrados en evolith.yaml → spec.compliance.adrRegistry (regla federada Core INH-04).
  • Fuente única de verdad (CD-18): el fichero docs/adrs/T-{NNN}-<slug>.md (+ su par .es.md) es el ADR; los demás registros son índices derivados que deben mantenerse en sincronía con él y con el disco: (1) este hub (DECISIONS.md/.es.md), (2) evolith.yaml → spec.compliance.adrRegistry, y (3) el catálogo servido por GET /api/architecture/adrs (AdrRegistryCatalog). Al añadir o cambiar un ADR se actualizan los tres, y todo fichero de docs/adrs/ debe tener par bilingüe — comprobado por check-bilingual-parity.mjs (cobertura ADR).

Índice de Decisiones Locales

ID Título Operación Ref Upstream ADR Local Notas
T-001 Orquestación de Monorepo con Nx Adoptar ADR-0001 T-001 Corregido 2026-08-01 (GAP-015): la entrada anterior decía «npm workspaces con Nx», y ningún package.json del repositorio declara workspaces — nunca se usaron. Nx orquesta por GRAFO DE PROYECTOS: project.json más los plugins de inferencia @nx/vite, @nx/webpack, @nx/eslint y @nx/jest declarados en src/nx.json. La razón que decidió es que el repositorio no es homogéneo: tracker-api es .NET, y npm workspaces enlaza node_modules entre paquetes npm — habría cubierto tres proyectos de cuatro dejando fuera al mayor.
T-002 Adopción de Microfrontends en Fase 1 Sobrescribir N/A T-002 Desviación de topología para escalabilidad de UI.
T-003 Arquitectura Hexagonal (Ports & Adapters) Adoptar ADR-0002 Capa de dominio pura sin dependencias externas.
T-004 TypeScript estricto como lenguaje primario Adoptar ADR-0003 strict: true habilitado.
T-005 TypeORM como ORM (Data Mapper pattern) Adoptar ADR-0043 Data Mapper elegido sobre Active Record.
T-006 React con Vite como base del frontend Adoptar ADR-0044 T-006 Topología microfrontends.
T-007 Zustand + TanStack Query (State Management) Adoptar ADR-0045 Zustand (cliente) + TanStack Query (servidor).
T-008 Convención de nombres de schema PostgreSQL Definir N/A T-008 Canónico: tracker_discovery, tracker_design...
T-009 REST + OpenAPI 3.0 como única API en Fase 1 Definir N/A T-009 GraphQL fuera de alcance en Fase 1.
T-010 Framework de agentes configurable por tenant Extender N/A T-010 Core usa BMAD internamente; el Tracker es framework-agnóstico. El tenant configura su harness agéntico por fase (bmad, spec-kit, custom).
T-011 Estándar de numeración para Iniciativas, ADRs y Specs Definir N/A T-011 IDs de gobernanza/diseño: Iniciativas INIT-{NNN}, ADRs locales T-{NNN}, ADRs upstream ADR-{NNNN}. La descomposición de ejecución queda fuera de alcance.
T-012 Contratos de eventos de dominio en libs/shared/ Definir N/A Eventos compartidos (DriftDetectedEvent, ExternalCheckpointRegisteredEvent) se definen como contratos TypeScript en libs/shared/src/domain/events/. Pact tests pendientes (Phase 0).
T-013 Value Object canónico ExternalReference en Shared Kernel Definir N/A Unifica shapes dispares en 6 contextos. ExternalReference con system, externalId, url, type, linkedAt, label, metadata. Ubicado en libs/shared/src/domain/external-reference.vo.ts.
T-014 UC-005 dividido en UC-005a y UC-005b Definir N/A Plan Release y Authorize Deployment son operaciones distintas con distintos actores y precondiciones. UC-005a (Planning), UC-005b (Execution).
T-015 Core BFF Gateway como único canal de comunicación Adoptar core/0074, nodejs/0075, core/0080 El Tracker se comunica exclusivamente con Evolith Core a través del BFF Gateway (NestJS). Se prohíben llamadas directas a servicios internos de Core. Actualizado 2026-06-28 (CR-26): la nota previa "B2B API Gateway (API key)" queda obsoletacore/0075 (api-key) fue superado por core/0080: Core no autentica; el BFF es el único perímetro (UMS Bearer + grafo de autorización) y las llamadas a Core usan repositoryRef+workspaceRef, sin API key.
T-016 Capa Anti-Corrupción para Integración PPM (Funnel 0) Definir N/A Se implementa una ACL (PpmIntakeACL) en el módulo tracker_intake para mapear y normalizar los esquemas de herramientas PPM externas (ej. Meisterplan) antes de que toquen el dominio del Tracker. Esto evita la contaminación de modelos financieros externos en el core del Tracker.
T-017 Core API Exposure Layer (REST-only) Adoptar core/0074 (CR-01/CR-02) Core-API es REST-only bajo /api/v1 (sin GraphQL/SSE) + gateway MCP. Salidas con envelope ADR-0073; errores RFC 9457. Reemplaza supuestos de transporte previos.
T-018 Contrato de Referencia de Repositorio Remoto Adoptar core/0080 (CR-26) Supera core/0075 (api-key). Core no autentica; el BFF es el único perímetro (UMS Bearer). Llamadas con contenido: repositoryRef {url, revision} + workspaceRef opaco + operationId.
T-019 Separación Dominio/Financiero Adoptar core/0078 (CR-17) ROI/finanzas fuera del dominio de gobernanza. Revisar Discovery Canvas / Business Case (hoy embeben ROI).
T-020 Corpus Multi-Topología Componible Adoptar core/0079 (CR-08/CR-09) Topología = 5 dimensiones componibles (progressive-axis/execution/integration/data/ai), no un ladder mono→micro. F1/F2/F3 = madurez de progressive-axis (modular-monolith/distributed-modules/microservices), no fases SDLC.
T-021 PhaseId Canónico Semántico Adoptar core (GT-343) (CR-28) Ids canónicos discovery|design|construction|qa|release; f1..f5 son alias deprecados solo de entrada; el namespace F# es madurez de topología.
T-022 Contrato de Evaluación de Satélite Adoptar core/0073, core/0074 (CR-03) POST /api/v1/evaluateEvaluationVerdict (OPA real vía SatelliteEvaluationPipeline). El verdict es evidencia técnica, no un GateDecision canónico: el Tracker decide el gate.
T-023 Distribución OPA-wasm Agnóstica Adoptar core/0085 (CR-03) Evaluación de reglas vía rulesets/opa/policy.wasm; resultado por regla passed|failed|skipped (skipped si falta el wasm — manejar con gracia).
T-024 Colisión de Nombre GateDecision Definir Accepted T-024 (CR-12/CR-13) Core ya define un VO GateDecision (estrecho). El GateDecision canónico del Tracker (rico: status/snapshots/approvals/exceptions) se renombra/namespacea (p. ej. TrackerGateDecision) y mapea el VO de Core como entrada de TechnicalEvaluation.
T-025 Gobernanza de IA Agéntica (cluster) Referenciar core/0081–0083, 0086–0089 (CR-21/CR-27) Sandbox isolation, trust boundary, action-authorization audit, telemetry/cost, ABAC tool execution, sovereign identity, event-driven workflows. Base para la superficie "AI Governance" del Tracker.
T-026 Redis solo para soporte operacional Definir N/A Redis es cache/locks/jobs/idempotency. Todo estado crítico va a PostgreSQL. Redis degrada gracefully cuando no está disponible. No es system of record.
T-027 Circuit breaker en cliente Core API Definir N/A Estado machine CLOSED→OPEN→HALF_OPEN. Umbral: 5 fallos. Recuperación: 30s. Previene cascade failures cuando Core no está disponible.
T-028 Estrategia PostgreSQL schema-per-context Superado por T-047 T-008 Declaraba 10 schemas. El código creó 4 y nadie lo escribió, así que el registro decía una cosa y la base otra. T-047 ratifica la topología consolidada.
T-029 OpenTelemetry Collector como sink único Superado por T-049 N/A Desacopla la app del backend de observability (Prometheus, Grafana, Loki). Un solo endpoint OTLP.
T-030 K8s overlays via Kustomize (no Helm inicialmente) Superseded por T-043 N/A Más simple para Fase 1. Helm chart se considera para Fase 2 si se necesita templating complejo. Superada 2026-07-19 (CD-29): la implementación eligió Helm y nunca hubo Kustomize — existen tres charts (product/infra/helm/evolith-tracker-{api,web,postgres}/Chart.yaml) y cero ficheros kustomization* en el repo. La decisión llevaba meses tomada en el código sin registro escrito; ver T-043.
T-031 Criterios de evaluación de compuertas configurables por tenant sobre campos de artefactos Definir Accepted T-031 Base de artefactos autoritativa del Core (requeridos inmutables + opcionales) + overlay del tenant por compuerta: criterios por campo/valor, campos custom, config obligatoria (fail-closed con UNCONFIGURED), 5 estados de resultado (MET/NOT_MET/PENDING/OBSERVED/NOT_APPLICABLE) y traza total por criterio.
T-032 Contexto acotado Geo / Datos Maestros (regionalización, localización, ubigeo) Definir Accepted T-032 Reemplaza los campos regionales libres (TenantLocalization) por catálogos maestros validados. Contexto geo dentro del Tracker con esquema propio + puerto IMasterDataDirectory + referencia blanda (geoId+snapshot); jerarquía por adyacencia+ltree, maestros globales tenant-agnósticos + overlays por tenant, datos por catalog_release. Extraíble a un servicio compartido evolith_mms (estrangulador) cuando exista un 2º consumidor.
T-033 Adopción de la Plataforma Shell del Core (Ddd/Aop/Factory/Bootstrapper) Adoptar Accepted T-033 El dominio consume BeyondNetCode.Shell.Ddd (kernel compartido) en vez de un kernel propio; stack Shell completo (aspectos, factory, bootstrapper) espejando UMS. Cierra R-10 / Core ADR-0071.
T-034 Hub de Configuración vs Monitor Definir Active T-034 La configuración/mantenimiento (CRUD) vive en Tenant configuration; los monitores son solo-lectura + comandos declarados por historias. §2.1 los registros están exentos; §2.2 toda sección de config es colapsable.
T-035 Estándar de info-hints de campo (ayuda en contexto) Definir Active T-035 Todo término técnico/campo avanzado lleva un ⓘ InfoHint junto al label (significado·impacto·cuándo); info prop en TextField/Select, accesible, copy bilingüe.
T-036 Parametrización de gates dirigida por artefactos del Core Definir Active T-036 (Extiende T-031) Evidencia y criterios anclados a artefactos definidos por el Core por fase (no texto libre); requerido efectivo = coreRequired OR tenantOverride; sin artefactos Core ⇒ sin criterios; GateMode (SIMPLE/COMPLEX) derivado de la config, no editable.
T-037 Consumir la proyección de Tenant de MMS (Tracker como consumidor downstream) Adoptar Proposed T-037 Adopta Core ADR-0106 por referencia: MMS es la única autoridad del Tenant maestro. Tracker añade mensajería (MassTransit/RabbitMQ + inbox EF), consume TenantEvent con upsert versionado idempotente y degrada el agregado Tenant local a proyección de solo lectura.
T-038 Binding a los contratos del Core vía schema neutro, no copia traducida a mano Definir Accepted T-038 @beyondnet/evolith-contracts es TypeScript-only e inconsumible desde .NET. Se pide a Core publicar JSON Schema de sus formas de cara al consumidor y se generan los tipos C# desde ahí. Puente provisional: DTOs derivados a mano confinados a la frontera de adaptador + guard de conformidad que falle el CI ante deriva (hoy es un no-op) + chequeo de schemaVersion en arranque. Cierra CD-12; prerrequisitos CD-01/CD-02.
T-039 El Core recomienda, el tenant decide — la autoridad de excepción vive en el Tracker Definir Accepted T-039 Todo veredicto del Core (incluido un waiver aplicado upstream) es advisory: nunca satisface por sí solo un criterio de compuerta. La autoridad de excepción es del tenant y se ejerce en el Tracker. Exception pasa a agregado de primera clase con solicitante/autorizador/alcance/caducidad/revocación; GatePolicy separa approvalAuthority de waiverAuthority (default: autorizador distinto). Corolario: un veredicto MockFallback tampoco puede satisfacer una compuerta y se marca como sintético. Cierra CD-19/CD-20/CD-21.
T-040 Adoptar el contrato de identidad de UMS v1 en vez de mantener un modelo propio Adoptar Accepted T-040 Tracker delega authn/authz en UMS, que es quien posee la abstracción de IdP (UMS ADR-0020): elegir IdP no es decisión de este satélite. UMS publica en 1.0.0 Ums.Sdk.Contracts/Authorization/.AspNetCore y Tracker es .NET, así que son consumibles directamente — pero no están publicados (sin dotnet pack/nuget push, 404 en nuget.org), de modo que la publicación es dependencia del board de UMS. Prohibido vendorizar o copiar a mano. Entregable inmediato y sin dependencia: alinear el claim de tenant a tid (UMS TE-01) y decidir el tratamiento de bid/cat/idp/jti. La reconciliación del modelo de permisos va aparte en CD-25.
T-041 Línea base heredada: triaje y registro de los ADR del Core core/0090core/0113 Referenciar core/0090–0113 T-041 (CD-16) Los 24 ADRs del rango, triados con motivo escrito en cinco cubos. Gobiernan código ya embarcado: core/0101 (Core stateless), core/0102 (agent runtime, ya consumido), core/0106 (proyección de Tenant), core/0107 (clúster único), core/0108 (topología de mensajes), core/0109 (satélites monorepo), core/0110 (pin MassTransit v8) → alta en evolith.yaml → adrRegistry. Gobierna con brecha abierta: core/0091 (rotación de tokens de workload) — el Tracker usa claves estáticas, así que se declara en governingAdrs pero no en adrRegistry, para no afirmar un cumplimiento inexistente. Gobiernan trabajo planificado: core/0098, 0100, 0103, 0104, 0105, 0111, 0113. No aplicables (9, con motivo): core/0090, 00920094, 00950097, 0099, 0112. Heredar no es autoría: todos siguen siendo decisiones del Core.
T-042 Guarda de build para el pin de licencia de MassTransit v8 Definir core/0110 T-042 (CD-16) MassTransit v9 es comercial y no sublicenciable; core/0110 fija la suite en v8 (Apache-2.0) y el Tracker cumplía por accidente con versiones exactas 8.3.1. Decisión local: rango acotado [8.3.1,9.0.0) en los tres PackageReference + guarda MSBuild solución-wide (src/apps/tracker-api/Directory.Build.targets, error EVOLITH0110) que falla el build si alguien reescribe la versión a mano. El modo de fallo que cierra no es un build roto, sino un build en verde embarcando licencia no redistribuible. Ampliar el rango exige superseder antes core/0110.
T-043 Helm supersede a Kustomize como formato de orquestación K8s Definir Accepted T-043 Supersede T-030. Ratifica una decisión ya tomada en el código y jamás registrada: tres charts Helm (product/infra/helm/) son los únicos artefactos desplegables y no existe ningún kustomization*. Alcance limitado al formato de empaquetado; no decide GitOps, gestión de secretos ni destino de despliegue. Cierra la premisa falsa que ARCHITECTURE_REVIEW.md (:155, :696, :714) inyectó en el board vía AR-03 (CD-29).
T-044 Un solo modelo de aislamiento entre tenants, con TenantScope como autoridad Definir Accepted T-044 Hay TRES implementaciones de la misma regla (aspecto, TenantScope, filtro EF). El fallo-abierto que las hacía peligrosas se cerró en SEC-01; lo que queda es incoherencia funcional. Requiere ratificación del PO: alinear el aspecto con TenantScope amplía lo que un operador de plataforma alcanza en tenant-intelligence, y eso es decisión de producto, no técnica.
T-045 Frontera de conectores: el Core posee la forma canónica; el Tracker posee conexión, credencial, fetch y sync Definir Accepted T-045 Verificado en el repositorio del Core: sus tres formas canónicas se declaran PURE y excluyen el fetch como paso de conector, y authorizesPhaseTransition: false está horneado en el tipo. Sin esta frontera el Tracker re-derivaría en .NET un mapeo ya canonizado, y el día que divergieran ganaría la copia local. Reduce el alcance de EAG-18/19/20/22 a la vez.
T-046 El Tracker habla REST con el Core mediante cliente propio tipado; el SDK publicado es sólo Node Definir Accepted T-046 @evolith/sdk-client nunca existió —verificado contra npm— y el SDK real es una librería Node que un backend .NET no puede consumir en proceso. El cliente propio no es un apaño: es el binding correcto. Cierra CR-05.
T-047 Topología de esquemas consolidada: 4 schemas, una convención, una excepción declarada Adoptar Accepted T-047 Supera a T-028. Ratifica los 4 schemas reales, renombra geotracker_geo, y declara masterdata como excepción CON MOTIVO: no es un contexto del Tracker sino una réplica de MMS. Cierra COH-015.
T-048 Contrato de evidencia de señal de calidad: mapeo, semántica y quién la produce Adoptar Accepted T-048 Cierra CD-07, CD-08 y CD-09 como una sola conversación. dimension y determinism pasan a columna; metrics/findings[] se proyectan. La señal es advisory en el cable y exigible por GatePolicy. El Tracker NO ejecuta productores — eso es del agent-runtime (ADR-0111).
T-049 Transporte de observabilidad: scrape Prometheus para métricas, push OTLP para trazas Adoptar Accepted T-049 Supera a T-029, que estaba en Definir SIN ADR — un boceto sin ratificar, no una decisión incumplida. Las trazas no tienen alternativa a push; las métricas siguen tirándose para no atar la disponibilidad del producto a la de un sumidero de telemetría. Exportador OTLP apagado por defecto. Cierra AR-14.
T-050 Taxonomía del dominio: cinco fases, una maquinaria, una frontera, un resultado y unos cimientos Adoptar Accepted T-050 Sustituye el listado de «9 bounded contexts pares», que nunca describió este producto: Gobernanza sola tiene 8 agregados frente a 3 de las cinco fases juntas, y cinco contextos construidos (Tenancy, Products, Intake, Audit, Geo) no figuraban en él. Integración es FRONTERA bidireccional, no resultado; Sdlc (la vía) y Governance (el árbitro) siguen separados porque T-031 explota esa distinción. Parte AR-05 en vocabulario de fase (mejora sobre lo que ya funciona en genérico) y capa de resultado (Metrics, inexistente).
T-051 El gateway MCP se enlaza al BFF del tracker-api, no al Core Definir Superseded T-051 Superado por T-052. Corrigió que las 6 tools MCP del Tracker tiraran de rutas imaginadas del Core, enlazándolas al BFF; su sujeto (el servidor MCP del Tracker) se eliminó después.
T-052 Un solo MCP del ecosistema (el del Core); el gateway consume, no compite Definir Accepted T-052 Elimina el servidor MCP del Tracker; el gateway es BFF agregador que consume core-api (REST) y evolith-mcp (cliente). Supera a T-051. Verificado en vivo.
T-053 Consumir la identidad UMS: JWKS/OIDC preferido, simétrico como interino Definir Proposed T-053 El tracker-api valida OIDC/JWKS pero el UMS no lo expone. Preferir que el UMS publique JWKS (aguas arriba); interino simétrico implementado y verificado con mock.
T-054 Gate de frontera en tiempo de edición: adoptar acotado, diferido a EAG-11 Definir Accepted T-054 Adopta el edit-gate del Core pero DIFERIDO: .claude/ está en .gitignore, EAG-11 aún no da la fuente única de reglas, y el matcher puede misfirear. Activación condicionada a EAG-11 + versionar .claude/settings.json + acotar rutas + vía de escape.

| T-055 | El Core deposita sus veredictos; el Tracker posee el ledger y deriva el tenant de la clave | Definir | Accepted | T-055 | Segunda dirección del tráfico de evidencia: POST /core-evaluation-transactions autenticado por clave de máquina atada al esquema POR NOMBRE, permiso propio :ingest SIN :read, tenant derivado de QUÉ CLAVE encajó (un tenantId en el cuerpo se rechaza con 400, no se ignora), idempotencia por (tenant, correlationId) respaldada por índice único, motor de cada regla VERBATIM (vocabulario abierto) y los dos responsables —quien pidió y quien debe arreglar— en columnas distintas. Estado ingested, distinto de completed. El DTO derivado a mano cumple T-038 con guarda de deriva por fixture. Sigue siendo advisory (T-039). Cierra GT-604. | | T-056 | El sellado no lleva lógica, la validación de contenido es del tenant, y la IA propone pero nunca decide | Definir | Accepted | T-056 | Tres capas con frontera dura, ratificadas al cablear GT-588. (1) El sellado no mira el contenido: payload es opaco y lo único que se fija es la regla de ESCRITURA (claves ordenadas en toda profundidad, comparación ordinal para que el idioma de la máquina no las reordene, una codificación). Por eso la interoperabilidad C#/TypeScript no crea un problema de estandarización por tenant — un notario sella documentos distintos con un procedimiento invariable. (2) La validación de contenido la configura el tenant: en cuanto la semántica de un cliente llega al motor como código, el motor pasa a ser un catálogo de casos particulares y cada cliente nuevo es una release. Misma frontera que T-039 aplicada al contenido. (3) La IA propone, nunca decide: un decisor probabilístico vuelve la garantía incomprobable, porque dos ejecuciones podrían diferir y nadie sabría cuál vale; detrás tiene que haber un verificador determinista y, si la propuesta es probabilística, su confianza se muestra a quien la ratifica (GT-590, GT-584). | | T-057 | The tenant signs what it asserts, the platform attests that it was recorded | Definir | Accepted | T-057 | Two signing identities, and only one belongs to the tenant. The ISSUER key is the tenant's and lives in tenant configuration, alongside the gate matrix and the artifact overlay — it is the tenant's own claim, so it is the tenant's key. Per-tenant keys make custody harder, not easier (N to store and rotate), and that cost is accepted because it is what makes each tenant independent. The TRANSPARENCY SERVICE key stays with the platform: RFC 9943 puts that authority in a SEPARATE entity, and MerkleTransparencyService already says why — a statement and a receipt signed with the same key are a self-assertion with ceremony, so a tenant signing its own receipt could rewrite its history and re-sign it. Neither key is what makes the trail immutable: that is the append-only audit table (MakeAuditEntriesAppendOnly); the signature adds attributable provenance, not immutability. AUD-TRANSP-04 already refuses to call it non-repudiation when both identities resolve to the same key. |

Plantilla para nuevos ADRs: Al crear un nuevo documento para "ADR Local", utilice el esquema de Frontmatter definido en los estándares de Evolith Core y ubíquelo en la carpeta de gobernanza correspondiente.