Fundamentos

Contexto del proyecto — LIS

Constitución de ingeniería. Es la fuente de autoridad sobre cómo se escribe código aquí. Si una instrucción de una sesión contradice este documento, gana el documento y se avisa.

Qué es

Sistema de Información de Laboratorio Clínico. Gestiona la trazabilidad desde el registro de la solicitud hasta la entrega y conservación del informe. Usuarios: personal de recepción, flebotomía, técnicos, profesionales validadores, calidad, almacén, caja y administración.

El 70 % de la información objetiva sobre la que un médico decide viene del laboratorio. La integridad y la trazabilidad no son requisitos accesorios de este software: son su obligación principal. Un resultado mal atribuido es un daño clínico, no un defecto de software.

SLO provisional: p95 < 500 ms en consultas operativas, 99.5 % en horario de laboratorio. DECISION REQUIRED antes de M4: confirmarlos con la operación real.

Stack

Java 25 · Spring Boot 4.1 · Maven · PostgreSQL 18 · jOOQ · Flyway · Spring Security OpenTelemetry · JUnit 5 · Testcontainers · ArchUnit · JaCoCo · Spotless

No hay JPA ni Hibernate. No se añaden. Todo acceso a datos es jOOQ.

Arquitectura

Monolito modular con puertos y adaptadores, organizado por capacidad de negocio.

com.sineltek.lis
├── compartido/     identificadores, errores, reloj, actor, consecutivos
├── catalogo/       prueba, perfil, analito, intervalos, tarifas
├── admision/       paciente, orden, prueba solicitada, consentimiento
├── muestras/       muestra, custodia, no conformidad, alicuota
├── analitica/      ejecucion, resultado, version de resultado
├── validacion/     referencia, criticidad, reglas, validacion, criticos
├── informes/       informe, detalle, entrega, correccion
├── calidad/ finanzas/ microbiologia/ derivacion/ inventario/ seguridad/
└── jooq/           GENERADO. No se edita a mano. No se revisa.

Cada módulo: dominio, aplicacion, infraestructura, api.

REGLA DE DEPENDENCIAS: api → aplicacion → dominio. dominio no conoce Spring, ni jOOQ, ni HTTP, ni PostgreSQL. Sin excepciones. aplicacion tampoco: los casos de uso son objetos corrientes, se cablean con un @Configuration en infraestructura y se prueban sin levantar contexto. Las clases de com.sineltek.lis.jooq solo se importan desde infraestructura.

REGLA ENTRE MÓDULOS: la escritura nunca cruza el límite de un módulo; se invoca el caso de uso del módulo dueño. La lectura sí puede cruzarlo a través de las vistas del esquema.

Ambas reglas son tests de ArchUnit. Si rompes una, el build falla. No se anota @ArchIgnore.

Reglas no negociables de ingeniería

  • Sin lógica de negocio en controllers ni en adaptadores. El adaptador traduce, no decide.
  • Los Record generados por jOOQ no salen de infraestructura. En el borde van DTOs.
  • La transacción empieza y termina en la frontera del caso de uso, nunca en el repositorio.
  • Errores tipados en dominio; se traducen a HTTP solo en api/.
  • Toda I/O con timeout explícito. Prohibido el timeout infinito.
  • La API no usa sesión HTTP: cada petición se autentica por sí misma (ADR-0005). La identidad la emite un proveedor externo y este servicio solo valida tokens (ADR-0009), así que no habrá autenticación por cookie y CSRF queda desactivado de forma permanente, no provisional.
  • Este servicio no custodia credenciales. Ni contraseñas, ni passkeys, ni secretos reversibles: cuenta_usuario es una referencia a una identidad externa. Passkeys, OTT, MFA y login federado son configuración del proveedor de identidad, no código de aquí.
  • Thymeleaf está para rellenar plantillas de mensaje (cuerpos de correo y avisos), en src/main/resources/plantillas. No sirve páginas: el borde del sistema es una API HTTP (D-03).
  • Observabilidad, no instrumentación opcional. Las tres señales llegan al backend por OTLP y se correlacionan: log con traceId, traza con su span, métrica con su histograma. Las observaciones se crean con ObservationRegistry de Micrometer —una observación da la métrica y el span—, se nombran lis.<modulo>.<operacion> y se ponen en los puntos de decisión, no en cada método. El dominio no loguea: devuelve errores tipados y registra quien orquesta. Convenciones completas en ADR-0007.
  • Sin abstracciones especulativas. Sin interfaces de una sola implementación que no sean puertos. Sin features no pedidas.

Reglas del dominio clínico

Estas no se negocian, no se optimizan y no se simplifican “por ahora”.

  1. Un hecho clínico no se borra ni se edita. Una corrección es una versión nueva o un evento nuevo. DELETE sobre datos clínicos, financieros o de inventario está prohibido.
  2. resultado_version es inmutable salvo su estado. Nunca se actualiza un valor. Una repetición crea otra ejecución y otra versión; la anterior se conserva.
  3. resultado.version_vigente_id es el puntero de lectura. Una versión creada y no apuntada no existe para los informes. Crearla y apuntarla ocurre en la misma transacción.
  4. Libros append-only: evento_muestra, movimiento_inventario, historial_estado_orden, historial_estado_prueba, evento_auditoria. Una corrección es una fila nueva.
  5. Estado e historial se escriben juntos, siempre por el motor de transiciones. Ningún otro código escribe una columna de estado de orden o de prueba.
  6. Quién opera y quién firma son identidades distintas. cuenta_usuario ejecuta; profesional_laboratorio aprueba, valida y firma. Una cuenta nunca sustituye al profesional como responsable clínico, aunque estén enlazadas.
  7. Los consecutivos legibles salen de siguiente_numero_documento. Nunca MAX + 1.
  8. Una referencia ausente o ambigua no es normal. Si no hay coincidencia única de intervalo, la versión queda en referencia_no_resuelta y va a revisión humana.
  9. Un resultado crítico nunca se autovalida y bloquea la liberación automática. La notificación crítica solo cierra confirmada o escalada.
  10. Coherencia de organización, paciente y orden en toda referencia. Nunca se vincula a una prueba una muestra de otro paciente u orden.
  11. Consentimiento exigible bloquea el avance. Sin él, la prueba no se procesa ni se divulga.
  12. Dinero en BigDecimal con la escala de la columna (numeric(14,2) y variantes). Prohibido double, float y aritmética en Double.
  13. Tiempos: eventos en timestamptz, siempre UTC en almacenamiento y transporte (OffsetDateTime/Instant). Fecha de nacimiento y vencimientos son fecha civil (LocalDate), sin hora y sin zona.
  14. Prohibida la PII en telemetría. Ni nombre, ni documento, ni historia, ni valores de resultado, ni diagnóstico: no en un mensaje de log, no en un atributo de span, no en una etiqueta de métrica y no en baggage. Un span se exporta igual que un log. Los identificadores técnicos sí, porque no identifican a una persona sin acceso a la base. Ante la duda, no se emite.
  15. Los resultados sensibles se restringen por prueba y por resultado, además de por rol, y toda consulta a ellos queda auditada.

Base de datos

  • Esquema lis, no public.
  • El esquema documenta el dominio: 90 comentarios de tabla y 12 de función. organizacion es «raíz de aislamiento de una instalación o entidad legal», cuenta_usuario «no almacena contraseñas ni secretos reversibles». Son definiciones, no notas: por ADR-0003 el DDL es fuente de reglas. Antes de declarar que algo no está definido, se busca ahí además de en docs/.
  • La base solo se modifica por migración Flyway. Nunca se edita una migración ya aplicada.
  • Tras cambiar el esquema: ./mvnw -Pjooq-codegen generate-sources y el código generado va en el mismo commit que la migración. Un esquema y un código generado desincronizados rompen el build de cualquiera que clone el repositorio.
  • Las restricciones y los triggers del esquema son parte del dominio. No se desactivan, no se rodean y no se borran para que pase un test. Si un trigger te bloquea, el error está en tu código o en tu entendimiento de la regla; para y explica.
  • El saldo de inventario lo calcula y lo escribe la aplicación: los movimientos de un mismo lote se serializan.
  • No se usan columnas generadas virtuales para datos que deban congelarse. Una instantánea histórica es una columna real.

Testing

  • TDD obligatorio en dominio y aplicacion. Cobertura ≥ 90 % (puerta activa en verify).
  • Ver el test fallar antes de implementarlo, y por la razón correcta, no por compilación.
  • Integración con Testcontainers contra PostgreSQL 18 real. Prohibido mockear el adaptador jOOQ o el repositorio: el esquema del LIS apoya reglas en triggers y restricciones, y un mock no prueba ninguna.
  • Fakes en memoria que respetan el contrato del puerto, no mocks que verifican llamadas.
  • Nombres: should_<comportamiento>_when_<condicion>, y el test cita en su nombre o en su javadoc el identificador que verifica (RN-xx, CU-xx, PO-xx).
  • Un test no se modifica para que pase. Si el test es incorrecto, dilo y para.

Trazabilidad

RN-xx (marco conceptual §16) → PO-xx / CU-xx (procesos) → S1.xx (slice) → test

Los requisitos ya existen en docs/. No se inventan requisitos ni reglas clínicas. Si algo no está en la documentación, es una pregunta, no una decisión de implementación.

Comandos

make              lista todos los targets con su descripcion
make dev          Postgres + pgAdmin + Grafana/OTel
make doctor       estado de contenedores, puertos y esquema
make verify       gate completo: compila, tests, arch-test, cobertura, formato
make migrate      aplica migraciones Flyway
make jooq-codegen regenera las fuentes jOOQ (requiere make dev)
make db-reset     borra el esquema lis y lo reconstruye desde las migraciones

No está terminado hasta que make verify pasa en verde.

Reglas permanentes de interacción

ALCANCE
1. Modifica solo los archivos necesarios para el cambio pedido.
2. No refactorices código no relacionado.
3. No actualices dependencias ni añadas librerías sin aprobación explícita.
4. No cambies el esquema sin una migración nueva. Nunca edites una ya aplicada.
5. No añadas JPA, Hibernate ni otro ORM.
6. Si el cambio pedido entra en conflicto con esta constitución: PARA y explica.

INCERTIDUMBRE
7. Si falta información, no inventes. Declara explícitamente:
   UNKNOWN / MISSING INFORMATION / ASSUMPTION / DECISION REQUIRED
8. No tomes decisiones de negocio, clínicas o de arquitectura en silencio.

ENTREGA
9. Con cada cambio entrega: supuestos, decisiones, trade-offs, riesgos y cómo verificarlo.
10. Muestra salida real de comandos ejecutados, no descripciones de lo que debería pasar.

Dónde no decides tú

Aquí la IA propone y genera casos adversariales; no es autora sin supervisión.

  • Límites de agregados y de módulo.
  • El modelo de autorización: es una decisión de producto y de normativa.
  • Reglas clínicas —intervalos, límites críticos, criticidad, autovalidación, delta, reflejas—. Se configuran y se aprueban con vigencia y profesional responsable. No se codifican a mano ni se inventan umbrales.
  • Política de rechazo de muestra, estabilidad, plazos de aviso crítico, informes parciales y restricciones de divulgación: se parametrizan conforme a la institución.
  • Migraciones destructivas sobre datos existentes.
  • Concurrencia fina y bloqueos.
  • Criptografía y manejo de secretos: primitivas estándar, nunca esquemas propios.

Documentos

docs/PLAN_MAESTRO.md plan e hitos · docs/MARCO.md método de trabajo · docs/LIS_marco_conceptual_unificado.md lenguaje ubicuo y reglas RN-xx · docs/LIS_procesos_procedimientos_flujos_casos_uso.md procesos PO-xx y casos CU-xx · docs/LIS_propuesta_modelo_datos_postgresql_18.md razones del modelo · docs/adr/ decisiones.