Saltar a contenido

ADR-0010 — Arquitectura: multi-servicio por dominio

Contexto

La plataforma es estructuralmente heterogénea: combina patrones técnicos muy distintos entre sí. Conviven una ingesta HTTP de petición/respuesta, un generador de carga, un procesamiento de streams en tiempo real, una orquestación de trabajos batch, entrenamiento de modelos (intensivo en CPU y GPU), servicio de inferencia de baja latencia, monitorización de deriva y toda la infraestructura de datos y observabilidad.

Cada uno de estos dominios tiene un perfil distinto en necesidades de recursos, patrón de escalado, tolerancia a fallos, cadencia de despliegue y dependencias. La ingesta escala con el tráfico de clientes; el procesamiento de streams con las lecturas por segundo; el entrenamiento es una tarea grande y vertical. Un fallo en la ingesta devuelve un error al cliente; un fallo en el streaming acumula trabajo recuperable; un fallo en el entrenamiento simplemente se reintenta.

La decisión arquitectónica fundacional es: cómo dividir el sistema en unidades de despliegue. Las restricciones incluyen un único desarrollador, una demo que debe levantar todo con un solo comando, el mensaje de portafolio de "diseño de sistemas a escala" y un coste operacional contenido.

Decisión

Se adopta una arquitectura multi-servicio por dominio: cada área técnicamente distinta vive en su propio contenedor, con su propio ciclo de vida. Ni un monolito con todo dentro, ni microservicios puros desglosados por capacidad de negocio.

Los límites se trazan según el tipo de tiempo de ejecución, la cadencia y el patrón de escalado: ingesta, generador de carga, procesamiento de streams, orquestador (con sus componentes), entrenamiento, servicio de inferencia, monitorización, registro de modelos, base de datos, capa de streaming, caché, almacén de objetos y observabilidad. En total, del orden de quince a dieciocho contenedores, agrupados en perfiles (núcleo, ML, observabilidad) para poder levantar solo un subconjunto en desarrollo.

La comunicación es HTTP síncrono para petición/respuesta, streaming asíncrono para el flujo de datos, base de datos compartida como fuente de verdad de las lecturas y registro de modelos como punto de intercambio del ML. No se introduce malla de servicios ni pasarela de API, innecesarias a esta escala.

Consecuencias

Positivas:

  • Cada contenedor escala de forma independiente; la mayor parte del tiempo solo la ingesta necesita crecer.
  • Aislamiento de recursos: el entrenamiento puede pedir GPU sin afectar a la ingesta.
  • Despliegue independiente: publicar un modelo nuevo no obliga a redesplegar la ingesta.
  • Aislamiento de fallos: la caída de un dominio no tumba el sistema.
  • Los perfiles de despliegue dan una experiencia de desarrollo ágil sin levantarlo todo.
  • El camino a un orquestador de contenedores completo es aditivo, no una reescritura.

Negativas o costes aceptados:

  • Sobrecarga operacional real: muchos contenedores implican más logs, más dashboards y más memoria total.
  • Coste de memoria apreciable si todo corre a la vez.
  • Cierta duplicación de contratos de datos entre productores y consumidores, mitigada con pruebas de integración.
  • La curva de entrada intimida frente a un monolito; se mitiga con documentación y perfiles.

Alternativas descartadas

  • Monolito puro (un servicio con todo): cada dominio competiría por los recursos, un fallo se propagaría a todo el sistema, el escalado sería imposible y, además, no cabría meter en un solo proceso las piezas que por naturaleza son clústeres o servicios propios.
  • Microservicios puros (un servicio por capacidad de negocio, con bases separadas y transacciones distribuidas): resuelven problemas de coordinación entre equipos que aquí no existen. Para un solo desarrollador aportan cero beneficio y un coste operacional y de complejidad enorme.
  • Monolito modular: excelente para una aplicación web con dominios afines, pero no encaja con una mezcla de streaming, batch y ML donde cada dominio necesita un tiempo de ejecución distinto.
  • Funciones sin servidor: dependencia total del proveedor, arranques en frío dolorosos para la baja latencia e imposibilidad de albergar la infraestructura con estado (bases de datos, brokers).