ums

ADR-0055: Patrón de API Híbrida GraphQL/REST

Campo Valor
Estado Aceptado
Fecha 2026-05-21
Contexto UMS Web App — Estrategia de Comunicación de API
Decisores Equipo de Arquitectura

Problema

El sistema UMS necesita soportar tanto consulta flexible de datos (con relaciones anidadas, filtrado, y selección de campos) como semántica clara de comandos transaccionales. Usar un único patrón de API para ambos lleva a either over-fetching (REST) o semántica de mutación poco clara (GraphQL).

Decisión

Adoptar un patrón híbrido de GraphQL para Queries, REST para Commands:

Implementación Lado Cliente

Frontend (React)
├── GraphQL Client (graphql-request v7)
│   ├── Todas las queries usan URL absoluta: `${window.location.origin}/graphql`
│   ├── Queries tipadas generadas desde schema
│   └── Cacheado vía TanStack Query
│
└── REST Client (Axios vía httpClient.ts)
    ├── Todas las mutaciones (POST, PUT, DELETE)
    ├── Inyección de token CSRF para requests que cambian estado
    ├── Headers de dev (X-User-Id, X-Language, X-Tenant-Id)
    └── Normalización de errores vía interceptors

Justificación

  1. Flexibilidad de query: GraphQL permite al frontend solicitar exactamente lo que cada pantalla necesita sin over-fetching ni requests N+1.
  2. Semántica transaccional clara: REST provee códigos de estado HTTP bien entendidos, claves de idempotencia, y semántica de retry para mutaciones.
  3. Protección CSRF: Los endpoints REST están naturalmente protegidos vía tokens CSRF; las subscriptions/queries de GraphQL son solo lectura y CSRF-safe.
  4. Integración TanStack Query: Las queries de GraphQL cachean naturalmente con el sistema de query keys de TanStack Query.

Consecuencias

Positivas:

Negativas:

Implementación

Alternativas Consideradas

Alternativa 1: Niveles de API Separados (Query Tier + Command Tier)

Separar queries y commands en dos servicios de API desplegados independientemente — un servicio GraphQL dedicado para queries y un servicio REST dedicado para commands.

Rechazada porque:

Cuándo esta decisión debe revisarse:

Esta alternativa se vuelve válida cuando se cumpla cualquiera de las siguientes condiciones:

Trigger Explicación
Throughput de lectura consistentemente 10x el de escritura Scaling horizontal independiente del query tier se justifica
Equipos separados poseen las superficies de query vs command La Ley de Conway hace la división natural, no forzada
Se inicia migración hacia microservicios La separación de niveles es un paso prerequisito
Requerimientos tecnológicos incompatibles emergen ej., query tier necesita un runtime o estrategia de caching diferente

Consideración específica para SaaS — aislamiento de carga de tenants:

En un contexto SaaS multi-tenant, queries GraphQL pesadas de un tenant grande podrían impactar la latencia de commands (login, provisioning) si ambos comparten el mismo proceso. Este riesgo se mitiga en la arquitectura actual por:

  1. Límites de complejidad de query de GraphQL enforce a nivel de schema HotChocolate.
  2. Timeouts diferenciados por tipo de operación.
  3. Rate limiting por tenant a nivel de API Gateway (ver TE-07: YARP API Gateway).

La separación de niveles permanece como el camino de escalada correcto si estos controles prueban ser insuficientes a escala.


Alternativa 2: GraphQL para Queries y Mutaciones

Usar GraphQL exclusivamente — queries y mutaciones — eliminando la capa REST.

Rechazada porque:


Relacionados