ADR-031: recorrido en vivo de una petición, reconstruido desde Tempo
Aceptado el 15/11/2026 (regla de Andrés: ADR necesario y no absorbido por otro, aprobado; lo comunicó la sesión de planeación). Propuesto ese mismo día por el carril `recorrido-en-vivo` (número confirmado por planeación: 030 es de `integracion`, 032 de `monitoreo-en-vivo`, 033 de `panel-de-control`).
Contexto
En el demo hay que mostrar la tesis (el legado sale del camino de la petición, docs/comparativo-as-is-to-be.md) con peticiones reales, no solo con diagramas. Grafana muestra la traza como cascada de spans y Kiali el grafo de la malla, pero ninguno de los dos la pone sobre la arquitectura por capas del entregable ni dice qué componente del diseño es cada tramo.
Hechos medidos el 15/11/2026 con la pila integrada de F4 y Tempo 3 en local (apps/flow-explorer/dev/):
- Una orden vive en varias trazas: la creación, el webhook de la pasarela (entra por
hooksy abre traza nueva) y el registro en el OMS. Lo que las une es elorderId, que viaja como llave de los mensajes de Kafka (messaging.kafka.message.key). - Ningún span lleva
correlationId. El agente de Java lo guarda si se le pide la cabecera (OTEL_INSTRUMENTATION_HTTP_SERVER_CAPTURE_REQUEST_HEADERS=X-Correlation-Id); order-service además lo usa como llave del tópico de auditoría. - El borde (Cloudflare, NLB, gateway de entrada, KrakenD) no emite spans hoy: la traza empieza en el servicio.
- Los atributos de los spans traen la sentencia SQL, la URL completa, IP y agente de usuario. Sin datos personales en la corrida medida, pero nada impide que un agente nuevo agregue uno.
- Tempo tarda entre 20 y 60 segundos en tener buscable una traza nueva.
Opciones
| Opción | A favor | En contra |
|---|---|---|
| A. Solo enlaces a Grafana y Kiali | Nada que construir | No muestra el diseño; un span no dice a qué componente o regla corresponde |
| B. Vista propia que llama a Tempo desde el navegador | Sin backend | Expone la API de Tempo al navegador; los atributos completos llegan a la pantalla; sin lista blanca del lado del servidor |
| C. Vista propia con un backend mínimo de solo lectura que consulta Tempo, filtra atributos y mapea cada span al archivo de datos de la arquitectura | Una sola fuente del diagrama; la lista blanca se aplica antes del navegador; Tempo queda dentro del clúster; la malla limita la salida a Tempo | Una carga más que construir y operar; el mapeo depende de los nombres de los spans y de los servicios |
Decisión
Opción C (apps/flow-explorer, contrato contracts/openapi/flow-explorer.v1.yaml):
- El diagrama sale de un único archivo versionado,
apps/flow-explorer/web/data/architecture.json(nodos, capas y aristas con tipo), ymake flow-explorer-checkfalla si no casa con los C4 de contenedores del entregable (TO-BE y AS-IS) o si un canal toca el legado en TO-BE. - El backend solo hace GET a las API HTTP de Tempo (
/api/searchcon TraceQL y/api/traces/{id}) y de Prometheus (/api/v1/query). Busca una petición porcorrelationId(atributoshttp.request.header.x-correlation-idymessaging.header.ce_correlationid, o la llave del tópico de auditoría), pororderId(llave de Kafka), portraceIdo la última orden, y une todas las trazas del mismocorrelationIdy de la misma orden. Los nombres de los atributos están en la configuración. - Lista blanca de atributos mostrables en el backend (
src/whitelist.js); todo lo demás se descarta antes de responder, y un valor permitido con forma de correo o de tarjeta también. - Cada span mapea a un nodo y a sus aristas por reglas fijas (
src/mapper.js). Un span sin nodo se lista aparte, no se esconde. El camino del borde, que no emite spans, se dibuja punteado como inferido, sin duración. - La carga vive en
edge, en la malla: la AuthorizationPolicyflow-explorer-from-ingress, que genera la malla (ADR-032), deja entrar solo al gateway de entrada, y un Sidecar conREGISTRY_ONLYdeja salir solo hacia Tempo y Prometheus. Así la malla, no solo el código, impide que el backend llame a un servicio de dominio, al legado o a Kafka. - Sin clúster, la vista reproduce una traza real grabada (
fixtures/tempo-orden.json), rotulada como ejemplo con su fecha y su origen. Es también el fixture de las pruebas. - Tráfico del momento sobre el mismo diagrama: peticiones por segundo, 5xx y p95 de la malla por par de cargas, y llamadas de los adaptadores al legado, leídos de Prometheus cada 5 segundos y asignados a aristas con el mismo archivo de datos. El color sigue los SLO de
docs/to-be/10-observabilidad.md(300 ms de p95, 99.9% sin 5xx). Las órdenes recientes de Tempo abren su recorrido con un clic. Guiado y en vivo comparten diagrama y controles, sin recargar.
Qué se sacrifica
- Una carga más en el borde, con su imagen y su entrada en el catálogo (carril
nube). - El mapeo se apoya en nombres de servicio, de tópico y de base de datos: si cambian, el verificador con el fixture lo detecta, pero el fixture hay que grabarlo de nuevo (
scripts/record-fixture.mjs). - Buscar por
correlationIdnecesita la cabecera capturada en el agente de Java, que hoy no está endeploy/compose/otel/java.envni en el chart; sin ella la búsqueda solo encuentra la traza por la llave del tópico de auditoría. - Los tramos del adaptador del OMS no aparecen: la autoinstrumentación de Node no cubre el cliente de Kafka de la plantilla. Se revisaría al instrumentarlo.