ADR-01 · Microservicios con database-per-service
Aceptado Firmado: 07-may-26 · Senior 1 (Giordano) + Toscana · Ingeniería · Repo: toscana-sih/sih-arch
Contexto
El SIH-HNSM debe servir a 25 dominios funcionales heterogéneos (admisión, expediente, farmacia, laboratorio, imagenología, facturación, etc.) con escalabilidad y aislamiento de fallos. Un monolito comprometería la disponibilidad y volvería costosos los cambios. Equipos pequeños deben poder evolucionar dominios de forma independiente.
Decisión
Arquitectura de microservicios con frontera por dominio acotado (DDD). Cada servicio posee su propia base de datos y nunca accede directamente a la base de otro. La comunicación entre servicios es por API REST/gRPC (sincrónica) o eventos Kafka (asincrónica).
Consecuencias positivas
- Aislamiento de fallos: un dominio caído no tumba el resto.
- Despliegues independientes y velocidad de iteración por equipo.
- Escalado horizontal por servicio según carga real.
- Cambios de esquema sin coordinar con otros dominios.
Consecuencias negativas
- Complejidad operacional alta: requiere observabilidad madura y CI/CD robusto.
- Consultas cross-dominio requieren agregación (BFF, vistas materializadas o CQRS).
- Consistencia eventual entre servicios; necesita patrones Saga/Outbox.
Alternativas evaluadas
- Monolito modular: rechazado por riesgo de acoplamiento y despliegue todo-o-nada.
- Base de datos compartida: rechazado, anula el aislamiento y crea acoplamiento de esquema.
ADR-02 · Java 25 LTS + Spring Boot 4 + Loom
Aceptado Firmado: 07-may-26 · Senior 1 (Giordano) + Toscana · Ingeniería · Repo: toscana-sih/sih-arch
Contexto
Necesitamos una plataforma backend de larga vida (3+ años de soporte LTS), con ecosistema maduro para sistemas hospitalarios, alto rendimiento bajo carga concurrente (admisión, lab, farmacia simultáneos) y experiencia disponible en el equipo y en el mercado guatemalteco.
Decisión
Backend en Java 25 LTS con Spring Boot 4 sobre JDK con Virtual Threads (Project Loom) habilitados en todos los servicios. Build con Gradle. Empaquetado con jlink + Docker multi-stage.
Consecuencias positivas
- LTS hasta 2030: estabilidad para el contrato HNSM.
- Loom resuelve el cuello de botella de hilos de plataforma sin reescribir a reactivo.
- Spring ecosystem: Security, Data, Cloud, Actuator listos.
- Compatibilidad amplia con drivers FHIR/HL7 maduros (HAPI-FHIR, HL7v2 OSS).
Consecuencias negativas
- Imágenes Docker pesadas si no se usa
jlink; mitigado con multi-stage. - Curva de aprendizaje en Loom para juniors; mitigado con mentoría del equipo de arquitectura.
Alternativas evaluadas
- Node.js + NestJS: rechazado por ecosistema clínico menos maduro.
- Go: rechazado por escasez de talento local y librerías FHIR/HL7.
- Kotlin: viable pero suma una dimensión de complejidad sin beneficio claro vs. Java moderno.
ADR-03 · React 19 + TanStack Query + Tailwind v4
Aceptado Firmado: 07-may-26 · Senior 1 (Giordano) + Toscana · Ingeniería · Repo: toscana-sih/sih-arch
Contexto
El frontend debe ser usable por personal clínico bajo presión, accesible (WCAG 2.2 AA), responsive (PC/tablet), con manejo eficiente de estado de servidor (caches de pacientes, recargas optimistas) y diseño consistente. Requerimos productividad alta con un solo Junior fullstack.
Decisión
SPA con React 19 (Server Components donde aporte), TanStack Query v5 para estado de servidor, Zustand para estado de UI local, Tailwind CSS v4 + shadcn/ui como sistema de diseño. Build con Vite 6. Tests con Vitest + Playwright.
Consecuencias positivas
- Diseño UX/UI dirigido por el Senior 1 (Giordano) con sistema de componentes consistente.
- TanStack Query elimina código boilerplate de fetching/cache.
- Tailwind acelera entrega; shadcn/ui asegura accesibilidad por defecto.
- HMR sub-segundo con Vite: feedback inmediato en clínica.
Consecuencias negativas
- Bundle puede crecer; requiere code-splitting por ruta y lazy-loading.
- Tailwind exige disciplina de sistema de diseño para no fragmentarse.
Alternativas evaluadas
- Angular: rechazado por verbosidad y curva más alta para fullstack junior.
- Vue: viable pero menor masa crítica de talento local.
- Server-rendered (Thymeleaf): rechazado, no cubre interacciones clínicas modernas.
ADR-04 · PostgreSQL como BD primaria
Aceptado Firmado: 07-may-26 · Senior 1 (Giordano) + Toscana · Ingeniería · Repo: toscana-sih/sih-arch
Contexto
Necesitamos una base relacional ACID para datos transaccionales clínicos y administrativos (pacientes, encuentros, órdenes, facturación), con extensiones para JSON, full-text, geo y auditoría. Open source y operable on-prem por el HNSM.
Decisión
PostgreSQL 17 como base primaria de los servicios transaccionales. Extensiones obligatorias: pg_stat_statements, pgcrypto, pgaudit, uuid-ossp. Backups con pgBackRest + WAL streaming a sitio secundario.
Consecuencias positivas
- ACID, JSONB, full-text en una sola tecnología.
- Comunidad y soporte enorme; talento abundante.
- pgaudit cubre requisitos MSPAS de trazabilidad.
- Replicación lógica y física maduras.
Consecuencias negativas
- Operación demanda DBA con experiencia en VACUUM, autovacuum, índices.
- Tunning de parámetros (
shared_buffers, work_mem) requerido.
Alternativas evaluadas
- MySQL/MariaDB: rechazado por extensiones inferiores.
- Oracle/SQL Server: rechazado por costo de licencias y bloqueo.
- CockroachDB: sobredimensionado para escala HNSM y reduce ecosistema.
ADR-05 · MongoDB para notas clínicas
Aceptado Firmado: 07-may-26 · Senior 1 (Giordano) + Toscana · Ingeniería · Repo: toscana-sih/sih-arch
Contexto
Las notas clínicas, evoluciones, historia narrativa y formularios de especialidad tienen estructuras altamente variables, anidadas y con versiones que cambian con el tiempo. Forzarlas a un modelo relacional rígido encarece y desalienta su uso. Sin embargo, requieren búsqueda, versionado y firma digital.
Decisión
MongoDB 7+ para el dominio de documentos clínicos narrativos únicamente (nota médica, nota de enfermería, evolución, formulario de especialidad). Datos estructurados (paciente, encuentro, orden) permanecen en PostgreSQL. Cada documento se firma con hash SHA-256 y timestamp; las modificaciones generan nuevas versiones (no se borra).
Consecuencias positivas
- Modelo flexible para formularios cambiantes sin migraciones costosas.
- Búsqueda full-text e índices compuestos eficientes.
- Versionado natural por documento.
Consecuencias negativas
- Dos paradigmas (SQL/NoSQL) en producción; equipo debe dominar ambos.
- Consistencia con PostgreSQL requiere patrones de evento (Outbox).
- Operación adicional: backups, replica set, monitoreo.
Alternativas evaluadas
- Postgres JSONB: viable pero pierde índices documentales avanzados y madurez de búsqueda.
- Couchbase/Cassandra: rechazado por complejidad operacional mayor.
ADR-06 · TimescaleDB para signos vitales
Aceptado Firmado: 07-may-26 · Senior 1 (Giordano) + Toscana · Ingeniería · Repo: toscana-sih/sih-arch
Contexto
Los signos vitales (FC, FR, SpO2, T°, TA), curvas de monitor, balances y mediciones de UCI generan series temporales de alto volumen y consultas por rango de tiempo. PostgreSQL puro escala mal con miles de filas/segundo por paciente. Necesitamos retención por compresión y consultas continuas (downsampling).
Decisión
TimescaleDB (extensión de PostgreSQL) para todas las series temporales clínicas: signos vitales, curvas de monitor, balances de UCI, lecturas de bombas de infusión. Hipertablas por mes. Compresión nativa después de 7 días. Continuous aggregates para reportes.
Consecuencias positivas
- SQL estándar (es Postgres): no se aprende un nuevo motor.
- Compresión 90 %+ en datos históricos.
- Continuous aggregates: dashboards rápidos sin recalcular.
- Reusa el mismo cluster Postgres del ADR-04 (otro nodo dedicado).
Consecuencias negativas
- Versionado acoplado al de PostgreSQL.
- Hipertablas requieren disciplina en diseño de claves de tiempo.
Alternativas evaluadas
- InfluxDB: rechazado por modelo propio, menos integrable.
- Prometheus: es para métricas operativas, no clínicas.
ADR-07 · Apache Kafka como bus de eventos
Aceptado Firmado: 07-may-26 · Senior 1 (Giordano) + Toscana · Ingeniería · Repo: toscana-sih/sih-arch
Contexto
Los microservicios deben comunicarse de forma asincrónica para flujos como "alta de paciente → notificar laboratorio + farmacia + facturación + estadística". Necesitamos un bus durable, ordenado por partición, con replay y schema registry. Latencia objetivo < 100 ms entre productor y consumidores.
Decisión
Apache Kafka 3.7+ como bus único de eventos. Schema Registry (Confluent o Apicurio) con esquemas Avro versionados. Convención de nombres sih.<dominio>.<evento>.v<n>. Patrón Outbox obligatorio para garantizar publicación tras commit en BD.
Consecuencias positivas
- Desacopla servicios: el productor no conoce a los consumidores.
- Replay completo permite re-procesar errores y construir nuevas vistas.
- Ecosistema maduro: Connect, Streams, kSQL.
- Escala horizontal por partición.
Consecuencias negativas
- Operación compleja (ZooKeeper/KRaft, particiones, retención).
- Schema evolution requiere disciplina.
- Para 25 servicios pequeños, infraestructura puede ser sobredimensionada al inicio.
Alternativas evaluadas
- RabbitMQ: rechazado por replay limitado.
- NATS JetStream: viable pero menos talento local y ecosistema FHIR/HL7.
- Redpanda: compatible Kafka, candidato si Kafka resulta pesado para HNSM (revisar en S08).
ADR-08 · Mirth Connect para HL7 v2
Aceptado Firmado: 07-may-26 · Senior 1 (Giordano) + Toscana · Ingeniería · Repo: toscana-sih/sih-arch
Contexto
Hospitales de Guatemala y proveedores (laboratorio, imagenología, monitor de UCI) intercambian datos vía HL7 v2.x (ADT, ORM, ORU, MDM). Codificar parsers a mano es costoso y propenso a errores; necesitamos un motor de integración probado.
Decisión
Mirth Connect (NextGen Connect Integration Engine) como motor de integración HL7 v2. Cada interfaz (entrada/salida) es un canal Mirth versionado en Git. La salida estándar al núcleo del SIH es JSON normalizado vía REST → microservicio integration-svc → Kafka. La salida hacia exterior se traduce a FHIR R4 (ver ADR-09).
Consecuencias positivas
- Parser HL7 v2 robusto y probado en miles de hospitales.
- UI gráfica acelera el alta de canales.
- Open Source con soporte comercial disponible.
- Aísla la complejidad HL7 fuera de los microservicios.
Consecuencias negativas
- Tecnología basada en JavaScript (Rhino) para transformaciones; requiere disciplina.
- Despliegue requiere su propia VM/contenedor.
Alternativas evaluadas
- Apache Camel + HAPI HL7: viable, mayor control programático pero menor agilidad.
- Construir parser propio: rechazado, reinventa la rueda.
ADR-09 · FHIR R4 para interoperabilidad externa
Aceptado Firmado: 07-may-26 · Senior 1 (Giordano) + Toscana · Ingeniería · Repo: toscana-sih/sih-arch
Contexto
El MSPAS y futuros integradores (otros hospitales, telemedicina, registros nacionales) demandan interoperabilidad moderna. HL7 v2 es legado; FHIR R4 es el estándar internacional vigente con perfiles definidos para muchos casos.
Decisión
Toda integración externa (fuera del HNSM) se expone exclusivamente vía FHIR R4. Implementación con HAPI-FHIR Server en un microservicio fhir-gateway-svc. Recursos iniciales: Patient, Encounter, Observation, Condition, Procedure, MedicationRequest, DiagnosticReport, AllergyIntolerance, Immunization. Perfiles MSPAS-GT cuando se publiquen.
Consecuencias positivas
- Cumplimiento del estándar internacional.
- HAPI-FHIR provee servidor REST completo, validación y persistencia.
- Aislamiento: el modelo interno puede evolucionar sin romper el exterior.
- Habilita SMART on FHIR para apps de terceros.
Consecuencias negativas
- Mapeo interno ↔ FHIR debe mantenerse.
- HAPI-FHIR es pesado en RAM; requiere afinamiento.
Alternativas evaluadas
- FHIR R5: rechazado por madurez de implementaciones (revisar 2027).
- API propietaria: rechazado, no interoperable.
ADR-10 · Keycloak para autenticación
Aceptado Firmado: 07-may-26 · Senior 1 (Giordano) + Toscana · Ingeniería · Repo: toscana-sih/sih-arch
Contexto
Necesitamos autenticación centralizada para personal clínico (médicos, enfermería, admisión, farmacia, laboratorio), pacientes (portal futuro) y aplicaciones de terceros (FHIR). Soporte SSO, MFA obligatorio para roles clínicos, federación con AD del HNSM y auditoría de inicios de sesión.
Decisión
Keycloak 25+ como Identity Provider único. Flujo OIDC con PKCE para SPA y móviles, client credentials para servicio-a-servicio. Realms separados: hnsm-staff, hnsm-patients, hnsm-integrations. MFA TOTP obligatorio para staff. Federación LDAP/AD con HNSM.
Consecuencias positivas
- SSO out-of-the-box.
- Federación con AD del HNSM sin código.
- Roles, scopes y políticas centralizadas.
- Open Source, soporte Red Hat disponible.
Consecuencias negativas
- Operación crítica: si Keycloak cae, nadie entra. Requiere HA y backups frecuentes.
- Curva de aprendizaje en realms y flujos.
Alternativas evaluadas
- Auth0/Okta: rechazado por dependencia SaaS y costo recurrente en USD.
- Spring Security custom: rechazado, reinventa rueda compleja.
ADR-11 · K3s on-prem (Kubernetes ligero)
Aceptado Firmado: 07-may-26 · Senior 1 (Giordano) + Toscana · Ingeniería · Repo: toscana-sih/sih-arch
Contexto
El HNSM exige despliegue on-premise por sensibilidad de datos clínicos. Kubernetes vanilla es excesivo para el tamaño del HNSM y costoso de operar. Necesitamos orquestación moderna, GitOps-friendly, con HA suficiente para producción hospitalaria.
Decisión
K3s (Kubernetes ligero certificado por la CNCF) como plataforma de orquestación on-prem. Cluster de 3 nodos master + 4 nodos worker (mínimo) en el superservidor HNSM. Almacenamiento persistente con Longhorn. Ingress con Traefik. Política de red con Cilium.
Consecuencias positivas
- Footprint pequeño: corre cómodo en hardware on-prem.
- Compatible 100 % con Kubernetes estándar (manifiestos portables).
- Instalación y operación más simples que k8s vanilla.
- GitOps con ArgoCD funciona idéntico (ver ADR-14).
Consecuencias negativas
- Soporte comunitario; Rancher/SUSE ofrece comercial si se necesita.
- Para clusters muy grandes (> 50 nodos) k8s vanilla es preferible.
Alternativas evaluadas
- OpenShift: rechazado por costo de licencias.
- Docker Swarm: rechazado, comunidad estancada.
- Nomad: viable pero menos masa crítica.
ADR-12 · Auditoría WORM con hash encadenado
Aceptado Firmado: 07-may-26 · Senior 1 (Giordano) + Toscana · Ingeniería · Repo: toscana-sih/sih-arch
Contexto
Los datos clínicos exigen trazabilidad inmutable: quién leyó/modificó qué, cuándo y desde dónde. Requisito MSPAS y de protección legal del hospital y del paciente. Las auditorías deben ser WORM (Write Once, Read Many) y resistir alteración interna.
Decisión
Microservicio audit-svc recibe eventos de auditoría vía Kafka (tópico sih.audit.event.v1) y los persiste en una tabla PostgreSQL append-only con hash encadenado al estilo blockchain: cada fila incluye hash_anterior + hash_actual = SHA-256(payload + hash_anterior). Backup diario WORM cifrado (GPG) a almacenamiento separado.
Consecuencias positivas
- Cualquier alteración rompe la cadena de hashes y se detecta.
- Cumple expectativa MSPAS de auditoría inmutable.
- Verificación periódica automatizable.
- Modelo simple: una tabla, un hash; sin dependencias exóticas.
Consecuencias negativas
- Reescribir la cadena ante errores requiere herramientas operativas dedicadas.
- Crecimiento monótono: requiere política de archivado a cold storage.
Alternativas evaluadas
- Hyperledger Fabric: sobredimensionado.
- AWS QLDB / managed ledger: rechazado por on-prem.
- Auditoría en tabla simple: rechazado, no detecta alteraciones internas.
ADR-13 · Observabilidad: Prometheus + Grafana + Loki + Tempo
Aceptado Firmado: 07-may-26 · Senior 1 (Giordano) + Toscana · Ingeniería · Repo: toscana-sih/sih-arch
Contexto
25 microservicios en producción exigen observabilidad madura: métricas (qué tan rápido), logs (qué pasó) y trazas (cómo se conectó). Sin esto, diagnosticar un caso clínico afectado es ciego. Solución debe ser open-source on-prem y gratuita.
Decisión
Stack Grafana Labs completo: Prometheus (métricas, scrape de /actuator/prometheus de cada servicio Spring), Loki (logs estructurados JSON), Tempo (trazas OpenTelemetry), Grafana (dashboards y alertas). Instrumentación con Micrometer + OpenTelemetry SDK. Alertas a Alertmanager → WhatsApp/correo del equipo de guardia.
Consecuencias positivas
- Stack open-source coherente, todo se integra entre sí.
- Trazabilidad end-to-end de una solicitud clínica.
- Dashboards reutilizables; comunidad publica plantillas Spring/JVM.
- Costo cero de licencia.
Consecuencias negativas
- Almacenamiento puede crecer rápido; políticas de retención necesarias.
- Curva de aprendizaje en PromQL y LogQL.
Alternativas evaluadas
- ELK (Elasticsearch + Kibana): rechazado por costo de RAM y cambio de licencia.
- Datadog: rechazado por SaaS y costo recurrente.
ADR-14 · CI/CD con GitHub Actions + ArgoCD
Aceptado Firmado: 07-may-26 · Senior 1 (Giordano) + Toscana · Ingeniería · Repo: toscana-sih/sih-arch
Contexto
Necesitamos pipeline automatizado: cada PR debe ejecutar lint, tests, análisis de seguridad (SAST + dependencias), construir imagen y publicarla. El despliegue a entornos debe ser GitOps: lo que está en Git es la única fuente de verdad de qué corre en cada cluster.
Decisión
CI: GitHub Actions en cada repo. Pipeline obligatorio: lint → test unitarios → build → SAST (CodeQL) → SCA (Dependabot/OSV) → container scan (Trivy) → push a registry. CD: ArgoCD instalado en cada cluster K3s, observa repo sih-deploy con manifiestos por entorno (DEV/QA/STAGING/PROD-HNSM). Promociones se hacen vía PR sobre el repo de deploy.
Consecuencias positivas
- GitOps puro: rollback = revert de commit.
- Trazabilidad total: qué se desplegó, cuándo y por quién.
- GitHub Actions gratis para repos privados pequeños/medianos.
- ArgoCD UI da visibilidad inmediata del estado del cluster.
Consecuencias negativas
- Dos productos que mantener (Actions + ArgoCD).
- Manifiestos K8s requieren disciplina (Helm o Kustomize).
Alternativas evaluadas
- Jenkins + Spinnaker: rechazado por complejidad operacional.
- GitLab CI/CD + Auto-Deploy: viable pero exige migrar repos a GitLab.
- Flux CD: alternativa válida a ArgoCD; elegimos ArgoCD por UI superior.
Política de cambios: un ADR aceptado no se modifica. Si la decisión cambia, se crea un nuevo ADR que
supersede al anterior, dejando el original como referencia histórica. La tabla
11.5 mantiene el índice oficial.