Skip to content

chore: retirar GraphQL — REST como única superficie (implementa D-007) - #19

Merged
beyondnetPeru merged 2 commits into
mainfrom
chore/retirar-graphql
Aug 10, 2026
Merged

chore: retirar GraphQL — REST como única superficie (implementa D-007)#19
beyondnetPeru merged 2 commits into
mainfrom
chore/retirar-graphql

Conversation

@beyondnetPeru

Copy link
Copy Markdown
Contributor

Opción 1 de las tres que planteé: retirar GraphQL. −3.485 líneas, 44 ficheros borrados, ningún endpoint nuevo.

La decisión ya existía: D-007

Preguntaste si no habíamos descartado GraphQL antes. Sí, y yo respondí mal. Miré los ADRs, vi 0055 y 0059 en Aceptado, y dije que no había decisión.

D-007, del 2026-07-14, estableció «API REST-only con CQRS en la capa de aplicación», revisando expresamente ADR-0055 y ADR-0059. La documentación de referencia lo recogió entonces y lo sigue diciendo:

  • solution-architecture.es.md — «GraphQL fue retirado»
  • quality-objectives.es.md — SLO de lectura movido de GraphQL a GET
  • threat-model.es.md — vector de DoS por GraphQL retirado
  • prd-ums-001.md — API declarada REST-only

Lo que nunca ocurrió fue el cambio en el código. Un mes después seguían en pie el esquema, /graphql, HotChocolate y las 17 clases de consulta. Y los dos ADRs seguían diciendo lo contrario, sin que nada lo señalara.

Este PR implementa D-007, y ADR-0166 escribe el registro ADR que le faltaba.

Por qué se pudo pasar por alto: TD-010 (nuevo)

El registro de decisiones vive en un DECISIONS.md que no está en este repositorio y nunca ha estado (git log --all no encuentra nada). Los 117 enlaces a ../../DECISIONS.md y ../../GAPS.md que hay en docs/ apuntan a la nada.

Los registros se citan como autoridad —objetivos de calidad, modelo de amenazas y PRD delegan en D-007/D-008 para decir qué es la arquitectura—. Quien no puede abrirlos no puede comprobar si ADRs, prosa y código coinciden. Aquí no coincidieron durante un mes.

Registrado como TD-010, con la recomendación de traer el registro al repositorio que gobierna.

La retirada es borrado puro: REST ya cubría el 100%

No se ha escrito ni un endpoint. Hay una carpeta Endpoints/**/Queries/ paralela con 22 grupos REST de consulta frente a las 17 clases GraphQL, y cada consulta del esquema tiene gemela que despacha la misma query de MediatR.

Esa carpeta paralela es también por qué mi primer inventario concluyó —mal— que había que añadir el GET de identidad visual: miré el fichero de comandos y no el de consultas. Ya existía en Queries/BrandingQueryEndpoints, despachando el mismo GetBrandingByTenantIdQuery que el resolver.

Alcance

Backend — 17 clases de consulta, GraphQlServiceCollectionExtensions, SafeGraphQlErrorFilter, TenantContextGraphQlInterceptor, QueryResultExtensions, el paquete HotChocolate.AspNetCore, el endpoint en sus dos rutas, dos pruebas de integración y el bloque S2325 del .editorconfig que sólo existía por la reflexión de HotChocolate.

FrontgraphqlClient y los nueve *.graphql.ts con sus pruebas. Ocho ya eran código muerto; el noveno, branding, pasa a REST. getBranding colapsa a null tanto el 200 con cuerpo vacío (inquilino sin identidad visual) como el 404 (inquilino inexistente), igual que hacía GraphQL; cualquier otro error se propaga.

Documentación — ADR-0166 bilingüe; ADR-0055 marcado Reemplazado; ADR-0059 anotado (su decisión era co-ubicar dos superficies y queda una, pero el rechazo a separarlas en tiers sigue vigente); ADR-0066 con su cláusula de GraphQL marcada como no aplicable y conservada como registro; TE-07 sin su ruta /graphql; vista de arquitectura actualizada; TD-002 cerrado.

TD-002 se cierra por desaparición del sujeto, no por arreglo. Nunca se encontró la causa raíz de por qué appConfigurations devolvía vacío. Si algún día se reintroduce GraphQL, esa entrada merece releerse antes.

Cobertura nueva

El GET de identidad visual —ahora el único camino de lectura— no tenía ninguna prueba. Se le añaden tres, incluida leer lo que el POST acaba de escribir.

Verificación

dotnet build 0 errores
Ums.Domain.Test 930/930
Ums.Application.Test 934/934
Ums.ContractTest 38/38
Ums.Presentation.IntegrationTest 314/316 (2 saltados)
front: typecheck / vitest / build / eslint 0 · 1626/1626 · OK · 0

En vivo, contra la API arrancada: POST /graphql404, POST /api/v1/graphql404, GET /health/live → 200, y el GET REST de identidad visual devuelve lo que el POST acaba de escribir.

🤖 Generated with Claude Code

La decisión NO es nueva. D-007, del 2026-07-14, estableció «API REST-only con CQRS en
la capa de aplicación», revisando expresamente ADR-0055 y ADR-0059. La documentación de
referencia lo recogió entonces y lo sigue diciendo: la arquitectura de solución afirma
que «GraphQL fue retirado», los objetivos de calidad movieron su SLO de lectura de
GraphQL a `GET`, el modelo de amenazas retiró el vector de DoS por GraphQL y el PRD
declara la API REST-only.

Lo que nunca ocurrió fue el cambio en el código. Un mes después seguían en pie el
esquema, el endpoint `/graphql`, HotChocolate y 17 clases de consulta; y ADR-0055 y
ADR-0059 seguían en estado Aceptado, contradiciendo a D-007 sin que nada lo señalara.
Este commit implementa D-007 y ADR-0166 escribe el registro que le faltaba.

## Por qué se pudo pasar por alto: TD-010

El registro de decisiones vive en un `DECISIONS.md` que NO está en este repositorio, y
nunca ha estado. Los 117 enlaces a `../../DECISIONS.md` y `../../GAPS.md` que hay en
`docs/` apuntan a la nada. Quien pregunte «¿se decidió algo sobre GraphQL?» mirando los
ADRs concluye que no, porque 0055 y 0059 dicen Aceptado. Queda registrado como TD-010:
un registro de decisiones fuera del repositorio que gobierna vuelve a divergir, y esta
es la evidencia.

## La retirada es borrado puro: REST ya cubría el 100%

No se ha escrito ni un endpoint. Existe una carpeta `Endpoints/**/Queries/` paralela con
22 grupos REST de consulta frente a las 17 clases GraphQL, y cada consulta del esquema
tiene gemela que envía la MISMA query de MediatR. El GET de identidad visual ya estaba
en `Queries/BrandingQueryEndpoints`; el resolver y el endpoint enviaban ambos
`GetBrandingByTenantIdQuery`.

Esa carpeta paralela es también por qué el primer sondeo de este trabajo concluyó —mal—
que había que añadir un GET: se miró el fichero de comandos y no el de consultas.

## Alcance

Backend: las 17 clases de consulta, GraphQlServiceCollectionExtensions,
SafeGraphQlErrorFilter, TenantContextGraphQlInterceptor, QueryResultExtensions, el
paquete HotChocolate.AspNetCore, el endpoint en sus dos rutas, dos pruebas de
integración y el bloque S2325 del .editorconfig que existía por la reflexión de
HotChocolate.

Front: graphqlClient y los nueve módulos `*.graphql.ts` con sus pruebas. Ocho ya eran
código muerto; el noveno, `branding`, pasa a REST. `getBranding` colapsa a `null` tanto
el 200 con cuerpo vacío (el inquilino existe sin identidad visual) como el 404 (el
inquilino no existe), igual que hacía el camino GraphQL; cualquier otro error se propaga.

Documentación: ADR-0166 en ambos idiomas, ADR-0055 marcado Reemplazado, ADR-0059
anotado —su decisión era co-ubicar dos superficies y queda una—, ADR-0066 con su
cláusula de GraphQL marcada como no aplicable, TE-07 sin su ruta `/graphql`, la vista
de arquitectura actualizada y TD-002 cerrado por desaparición del sujeto.

El GET de identidad visual no tenía ninguna prueba: se le añaden tres, incluida la de
leer lo que se acaba de escribir.

Verificado: build 0 errores; Domain 930/930; Application 934/934; Contrato 38/38;
Integración 314/316 (2 saltados); front typecheck 0, vitest 1626/1626, build OK,
eslint 0. En vivo: `/graphql` y `/api/v1/graphql` responden 404, health 200 y el GET
REST de identidad visual devuelve lo que el POST acaba de escribir.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…undía 133 en CI

La prueba «leer lo que el POST acaba de escribir» que añadí escribía identidad visual
sobre el primer inquilino sembrado. Las 31 clases de esta suite comparten
`UmsApiWebApplicationFactory` y su store, así que esa escritura contaminaba a todas las
demás: 133 de 316 en rojo en CI.

En local pasaba, en Debug y en Release. El orden de ejecución lo escondía. Una escritura
añadida, 133 fallos, y una corrida local verde: esa combinación es exactamente lo que
hace caro este tipo de defecto.

Se retira la prueba mutante y quedan las dos de lectura, que son deterministas. El
camino de escritura ya está cubierto en Ums.Application.Test/Tenants/Branding, a nivel
de handler y sin estado compartido. La razón queda escrita en el propio fichero para que
nadie la reañada y repita los 133.

Registrado como TD-011: no es la primera vez. Los comentarios de la propia factoría
documentan un incidente anterior de ~80 fallos por un constructor estático que filtraba
`Persistence__Provider` entre hosts. El mecanismo cambia, la forma no.

Verificado: Integración 313/315 (2 saltados) en Release.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@beyondnetPeru
beyondnetPeru merged commit de8119a into main Aug 10, 2026
24 checks passed
@beyondnetPeru
beyondnetPeru deleted the chore/retirar-graphql branch August 10, 2026 17:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants