WQuestions

Parte V · En la práctica

18

Una historia clínica

El spa y el taxi eran negocios transaccionales: eventos que ocurren, se cobran y se olvidan. La medicina es lo contrario. Aquí lo valioso no es la cita, sino el conocimiento denso que nace dentro de ella —y que tiene que sobrevivir décadas.

Son las 09:40 de un martes en cardiología. Llega Gonzales, la misma que meses atrás entró por urgencias con una fibrilación. Hoy viene a un control de rutina.

No hay drama. Se sienta y cuenta que duerme mal, que a veces siente el corazón «a destiempo».

La doctora le toma la presión y repasa su última analítica. La hipertensión sigue ahí, algo peor. Ajusta la dosis de un fármaco. Anota una alergia que la paciente no conocía. Agenda otro control en treinta días.

Quince minutos. Gonzales sale por la puerta y la cita termina.

Pero casi nada de lo que importa termina con ella. El diagnóstico seguirá vigente un año. La receta habrá que revisarla cada mes. La alergia recién descubierta tendrá que gritar cada vez que alguien intente recetarle ese fármaco.

Esos quince minutos dejaron un sedimento. Un médico distinto, en un hospital distinto, dentro de cinco años, tendrá que poder leerlo sin dudas. Ese sedimento es el examen de este capítulo.

Los negocios de esta parte del libro giraban alrededor de la transacción. Una sesión de spa empieza, termina, se cobra. Un viaje en taxi ocurre, se paga, se archiva. Modelarlos fue repetir una fórmula cambiando las palabras.

La medicina no concede esa comodidad. Una consulta también es un evento, pero a nadie le importa cuánto duró. Importa lo que se produjo dentro: un diagnóstico, una receta, una alerta de alergia, un control programado.

Y ese contenido tiene que vivir mucho más que la cita que lo produjo.

Por eso este capítulo cambia de foco. Aquí no exhibo una solución terminada, sino cómo se modela un dominio nuevo desde cero.

La medicina sirve para eso porque nadie llega con el esquema resuelto. Hay que sacarlo a la luz decisión por decisión, viendo dónde acierta la intuición y dónde el modelo nos corrige a tiempo.

La pregunta que gobierna el capítulo

¿Aguanta el modelo un dominio para el que no fue diseñado, sin inventar maquinaria nueva? La apuesta es que sí. Una consulta médica completa cae sobre los mismos siete ejes y el mismo lexicon que ya conoces, con toda su incertidumbre y su vigencia en el tiempo. Donde haya fricción, la mostraré tal cual.

Cómo se elicita un dominio que no conoces

Modelar un dominio nuevo no empieza escribiendo tripletas. Empieza con tres preguntas al negocio, en este orden, antes de tocar el teclado. Sirven igual para una clínica que para una aseguradora o una fábrica.

🗂️

¿Cuál es el evento ancla?

El acontecimiento alrededor del cual orbita todo lo demás. En el spa era la venta; aquí es la consulta. De él colgarán las demás piezas.

🧩

¿Qué nace dentro del evento?

Los «objetos de información» que la consulta produce: síntoma, medición, diagnóstico, prescripción, control. Cada uno con vida propia.

🔮

¿Sobre qué se preguntará mañana?

La prueba decisiva de qué merece volverse entidad. Si alguien va a interrogar un dato en el futuro, ese dato necesita identidad propia.

La tercera es la más sutil y queda para el final del capítulo. Por ahora basta la primera: el evento ancla. En medicina, ese evento es la consulta.

La consulta como carpeta maestra

Igual que en el taxi un viaje abrazaba a todos sus pasos menudos, la consulta médica actúa como una gran carpeta articuladora. Una revisión típica produce cinco piezas internas, y conviene nombrarlas como las nombraría el propio médico:

1 · El síntoma. Lo que dice el paciente, en sus palabras: «duermo mal y siento el corazón a destiempo». Subjetivo, fechado, con una intensidad que mañana querremos comparar.
2 · La medición. Los números crudos que toma la doctora: presión en 148/95. Objetivos, con unidad y momento.
3 · El diagnóstico. La conclusión clínica: hipertensión de grado 2. Una inferencia, no un hecho duro, y con fecha de caducidad.

4 · La prescripción. La conducta indicada: subir el antihipertensivo a 20 mg, una vez al día. Tiene un porqué, una finalidad y un control de seguridad.
5 · El control. El plan a futuro: nueva cita en treinta días. No es algo que pasó, sino algo previsto, y el sistema debe saber distinguirlo de lo real.

En el modelo, estas cinco piezas no son campos de texto de la consulta. Son cinco situaciones reificadas independientes que cuelgan del evento ancla con el viejo y confiable cable parte_de. La consulta reúne; cada pieza vive su propia vida.

tripletas
(consulta_8842) ∈ O
  instancia_de : consulta_cardiologia    # K · qué clase de evento es
  agente       : dra_navarro             # Q · quién atiende
  paciente     : maria_g                    # Q · a quién se atiende
  lugar_de     : consultorio_card_2      # L · dónde ocurre
  inicio       : 2026-06-09T09:40-05:00  # T · cuándo
  motivo       : control_hipertension    # K · por qué viene

(sintoma_8842    ∈ O,  parte_de,  consulta_8842)
(medicion_8842   ∈ O,  parte_de,  consulta_8842)
(diag_hta_8842   ∈ O,  parte_de,  consulta_8842)
(prescrip_8842   ∈ O,  parte_de,  consulta_8842)
(control_8842    ∈ O,  parte_de,  consulta_8842)

La arquitectura no se inmuta: una entidad grande que contiene un racimo de piezas menores, igual que la venta del spa contenía sus líneas.

La consulta cumple de sobra las condiciones para volverse situación en O. Agrupa varias partes, está fechada, tiene participantes con roles y, sobre todo, otras cosas le harán referencia.

Lo interesante de la medicina no es el contenedor. Es el interior de cada pieza.

consulta_8842 el evento ancla · O Q paciente maria_g Q agente dra_navarro L lugar_de consultorio_2 T inicio 09:40 · 9 jun síntoma lo que dice medición 148/95 diagnóstico HTA grado 2 prescripción 20 mg / día control previsto · +30 d parte_de M(O→O) Q los participantes · L el consultorio · T el momento · O / K las cinco piezas · M el cable que las cuelga del ancla.
Figura 18.1. La consulta reificada como evento ancla. Arriba, sus participantes repartidos en los ejes (maria_g y la doctora en Q, el consultorio en L, el momento en T). Abajo, las cinco sub-situaciones que nacen dentro de ella y cuelgan con parte_de. La consulta no «guarda» el diagnóstico como un campo: lo articula como una entidad vecina con vida propia.

El bache: cuando el eje O se queda corto

Aquí apareció la primera fricción real al programar este dominio. Vale la pena contarla, porque enseña más que cualquier acierto.

La intuición dice: «el tema de esta medición es la presión arterial». Y con la receta: «el tema de esta prescripción es el fármaco». Parece obvio.

Pero detente. «Presión arterial» y «el antihipertensivo» no son cosas físicas con identidad propia. No puedes guardar «la presión arterial universal» en un cajón. Es una magnitud, una categoría teórica.

La caja de pastillas que Gonzales compra en la farmacia sí es un objeto del eje O: tiene lote, vencimiento, ubicación. El concepto científico del fármaco, en cambio, es una clase, un habitante del eje K.

El instinto mezcla lo físico con lo conceptual sin darse cuenta.

El catálogo del modelo no se deja engañar. El cable tema solo admite conectar O con O. Si intento apuntar de una medición (en O) hacia la magnitud presión (en K), el sistema aborta y me regaña por mezclar peras con manzanas.

El modelo se niega a aceptar el disparate

Conectar una situación de O con una clase de K usando un cable que exige O→O lanza un SignatureError. No es un estorbo: es la frontera que impide que un dato mal puesto entre al grafo. El error no estaba en el modelo, sino en mi cabeza, que confunde el objeto concreto con la idea abstracta.

La salida no es forzar el cable genérico. Es declarar cables de dominio: roles propios de la medicina, hechos para apuntar hacia el eje K. Cada industria muy técnica reclama un puñado de cables exclusivos para sus conceptos abstractos.

python
# Mal · forzar el cable genérico "tema" hacia una clase → SignatureError
# Bien · declarar cables de dominio cuya firma apunta a K

u.assert_fact(medicion_8842, "medida_evaluada",      presion_arterial)  # O → K
u.assert_fact(prescrip_8842, "medicamento_prescrito", antihipertensivo) # O → K
prescrip_8842O medicamento_prescritoM(O→K) antihipertensivoK

Dos lecciones de la fricción

  • El catálogo universal cubre casi todo, pero no todo. Los roles base sirven para la inmensa mayoría de los hechos; cada dominio altamente técnico (medicina, química, derecho) pedirá unos pocos cables propios para sus abstracciones. Eso es esperado, no un fallo.
  • El error fue del modelador, no del modelo. La firma estricta no es burocracia: es la que distingue «la caja de pastillas» (objeto, O) del «fármaco como concepto» (clase, K). Sin esa distinción, una IA no podría razonar sobre interacciones farmacológicas.

Diseccionar un diagnóstico: duda, evidencia y tiempo

Un diagnóstico es la pieza más rica de toda la consulta, porque cruza a la vez tres dimensiones que un sistema de juguete suele aplastar en un solo campo de texto. El modelo las resuelve por separado, sin esfuerzo.

1 · El nivel de duda

Un diagnóstico casi nunca es una verdad tallada en piedra. Es lo que la doctora cree en ese instante, con la evidencia que tiene.

Para no perder esa duda clínica, marcamos la situación con su grado de certeza: puede ser sospechado, confirmado o descartado.

La diferencia entre «sospecho una arritmia» y «confirmo una arritmia» no es un matiz de prosa. Cambia qué se puede recetar y qué hay que vigilar.

tripletas
(diag_hta_8842, modalidad,       epistemica)   # K · pertenece al saber, no al deseo
(diag_hta_8842, estatus_factual, confirmado)    # K · sospechado | confirmado | descartado

Es la misma distinción que en el spa separaba la venta real de la simple intención de compra. Que un dato pueda ser «todavía no seguro» sin ser falso hace falta en cualquier dominio serio.

2 · La evidencia

Un diagnóstico no nace de la nada. Nace de los números. Si la doctora escribe «hipertensión», es porque vio la medición de presión.

Esa conexión no se guarda en un comentario. Se ata con un cable. Como vimos al hablar del «por qué», no hay un eje para eso: el porqué se reparte en cables distintos, y aquí toca el que apunta a la razón de la conclusión.

tripletas
(diag_hta_8842, motivado_por, medicion_8842)   # M · la medición es la evidencia

Con ese cable, la medición deja de ser un número suelto y pasa a ser la evidencia del diagnóstico.

Si un auditor médico revisa el expediente y pregunta «¿en qué se basó esto?», nadie tiene que interpretar prosa. Se lee siguiendo el cable. La justificación viaja con el dato.

3 · El viaje en el tiempo

Aquí se separa el software hospitalario de verdad del de juguete. Los diagnósticos caducan y cambian.

Tras el episodio agudo, en marzo, a Gonzales le asignaron hipertensión de grado 1. En esta consulta de junio la doctora confirma que empeoró: ahora es grado 2.

Si el sistema escribiera el diagnóstico nuevo encima del viejo, borraría la historia de la paciente. Eso no es solo mala medicina. Deja el caso sin manera de reconstruirse.

La regla de vigencia entra a salvarnos. No se sobrescribe nada: se guardan ambos diagnósticos, cada uno con su rango de validez, y se los conecta para que la cronología sea legible.

python
# El diagnóstico viejo: válido desde marzo, cerrado cuando se emite el nuevo
u.assert_fact(diag_marzo, "diagnostico_asignado", hta_grado_1,
              valid_from="2026-03-12", valid_to="2026-06-09")

# El diagnóstico nuevo: vigente desde hoy, sin fin (sigue activo)
u.assert_fact(diag_hta_8842, "diagnostico_asignado", hta_grado_2,
              valid_from="2026-06-09")

# Y se enlazan, para que la cronología no quede a interpretación
u.assert_fact(diag_hta_8842, "rectifica", diag_marzo)   # M · este corrige a aquel

El premio viene de fábrica. Si mañana hay una demanda y el juez pregunta «¿qué sabía el hospital sobre este paciente en abril?», la base no devuelve el estado de hoy.

Devuelve la verdad clínica que regía en abril, grado 1, ignorando todo lo que se supo después. Basta una consulta con un filtro de fecha.

tiempo → mar 2026 abr 2026 jun 2026 hoy HTA grado 1 valid_from: mar · valid_to: jun HTA grado 2 valid_from: jun · sin fin rectifica consulta · at = abril devuelve → grado 1 El eje del tiempo es T; las dos verdades son clases en K · O; el cable que las une es M. Nada se sobrescribió.
Figura 18.2. La vigencia del diagnóstico (D6) sobre la línea de tiempo. El grado 1 fue válido de marzo a junio; el grado 2 lo sustituye desde junio, con su ventana abierta. El cable rectifica conecta ambos. Una consulta fechada en abril (la línea vertical) atraviesa solo la barra del grado 1 y devuelve esa verdad —no la de hoy—. La reconstrucción del pasado es una proyección, no un archivo de respaldo.

La prescripción y la tormenta de los porqués

Cuando la doctora ajusta el fármaco se activan varias fuerzas causales a la vez. Una receta no es un dato suelto. Es una decisión con un motivo, un objetivo y un control de seguridad, y el modelo guarda cada uno en su propio cable.

python
pres = ingest_situation(u, lex, "prescribir",
    roles={
        "agente":               dra_navarro,
        "paciente":             maria_g,
        "medicamento_prescrito": antihipertensivo,   # O → K
        "dosis":                "20 mg",
        "frecuencia":           cada_manana,
    },
    sit_id="prescrip_8842",
)

u.assert_fact(pres, "motivado_por",      diag_hta_8842)        # ← por qué se receta
u.assert_fact(pres, "con_finalidad",     meta_presion_objetivo) # ← para qué se receta
u.assert_fact(pres, "verificado_contra", regla_renal_alerta)    # ← qué ley clínica la valida

Tres respuestas a tres preguntas distintas. La doctora no receta por gusto: lo hace motivada por el diagnóstico, con la finalidad de bajar la presión a una meta, y verificando que el ajuste no dañe unos riñones que ya vienen tocados.

Tres cables, tres porqués, ningún campo de texto ambiguo.

Contraindicación como objeto, no como código

La regla de seguridad («no combinar este fármaco con tal condición renal») no está escondida en el código de la aplicación. Es una situación más en el eje O, con su clase, su fármaco implicado, su condición disparadora y su orden. Como cualquier norma, tiene autor y fecha de vigencia, y vive en el grafo a la vista de todos.

tripletas
(regla_renal_alerta) ∈ O
  instancia_de        : contraindicacion_clinica   # K · qué clase de norma es
  medicamento_implicado: antihipertensivo          # K · sobre qué fármaco recae
  condicion_gatillo   : funcion_renal_reducida      # K · qué condición la dispara
  orden_oficial       : revisar_antes_de_recetar    # K · qué exige
  vigente_desde       : 2025-01-01                   # T · desde cuándo rige
  emitida_por         : guia_cardiologia_nacional    # Q · qué autoridad la dicta

Con la regla dentro del grafo, la base se vuelve lista sin volverse rígida.

Cuando la doctora pulsa «recetar», un motor externo (o una IA) recorre el grafo, encuentra la norma, comprueba la función renal del paciente y levanta una alerta antes de que ocurra el daño.

El grafo guarda la verdad. La lógica que la aplica vive afuera. Esa separación es deliberada, igual que en el spa: el modelo no ejecuta la regla de negocio, la deja legible para que quien la ejecute no se equivoque.

El control futuro: cuando el dato aún no es real

La quinta pieza, el control a treinta días, parece trivial y esconde algo importante. Todavía no ocurrió: es un plan. Si el sistema lo guardara como hecho consumado, ensuciaría cualquier informe. Contaríamos como «atendida» una cita que no existe.

El modelo lo marca como previsto, no como real, con la misma maquinaria que usamos para el diagnóstico sospechado.

tripletas
(control_8842) ∈ O
  instancia_de    : consulta_cardiologia
  paciente        : maria_g
  agente          : dra_navarro
  inicio_previsto : 2026-07-09           # T · la fecha agendada
  estatus_factual : previsto             # K · ¡aún no es real!
  motivado_por    : diag_hta_8842        # M · se controla esta hipertensión

Gracias a ese rótulo, el sistema responde sin tropezar a «¿qué citas tiene Gonzales agendadas?», donde el control aparece, y a «¿cuántos pacientes atendió la doctora en junio?», donde no aparece porque aún no sucedió. Lo previsto y lo real conviven sin confundirse.

La historia clínica como serie temporal

Hasta aquí hemos diseccionado una consulta. Pero Gonzales no es una consulta. Es una paciente con años de roce con el sistema sanitario.

La misma que entró por urgencias con una fibrilación. La que en marzo recibió su primer diagnóstico de hipertensión. La que hoy vuelve al control de cardiología. La que dentro de unas páginas pasará unos días ingresada.

Su identidad es una sola, cosida con el mismo maria_g que ya recorre la tienda y la clínica. Lo que cambia es el rosario de situaciones que cuelgan de ella con los años.

Eso es una historia clínica: no un documento, sino una fila de episodios en el tiempo.

En el modelo, la historia no es una tabla con una fila por visita. Es el conjunto de todas las situaciones cuyo paciente es maria_g, puestas en orden por el eje T y filtradas por la vigencia que cada hecho declara.

Cada episodio (una urgencia, una analítica, una consulta, un ingreso) es una situación más en O que apunta al mismo paciente. Recuperar «la historia de Gonzales» es barrer el grafo por ese nodo y leer la cinta.

tripletas
# Cada episodio es una situación que apunta al MISMO paciente, situada en T
(urgencias_2403, paciente, maria_g)   # T: 2026-02-14 · la fibrilación
(analitica_2511, paciente, maria_g)   # T: 2026-03-05 · sangre y lípidos
(consulta_3019, paciente, maria_g)    # T: 2026-03-12 · 1er dx de HTA grado 1
(consulta_8842, paciente, maria_g)    # T: 2026-06-09 · el control de hoy
(ingreso_9120,  paciente, maria_g)    # T: 2026-06-14 · el ingreso (más abajo)

# La historia = proyección por paciente, ordenada por T, con la vigencia de cada hecho
historia = u.timeline(maria_g, valid_at="2026-06-19")

Esa cinta lleva dos tiempos a la vez. Registra cuándo ocurrió cada episodio, en el eje T, y también desde cuándo y hasta cuándo se tuvo por cierta cada afirmación clínica.

Es la misma vigencia (D6) que salvó al diagnóstico de pisarse a sí mismo, ahora aplicada a la historia entera.

Por eso caben dos preguntas distintas. «¿Qué le pasó a Gonzales en abril?» corta por el tiempo del hecho. «¿Qué creía saber el hospital sobre Gonzales en abril?» corta por el tiempo del registro.

Un expediente de juguete confunde las dos. El grafo las separa de fábrica.

maria_g una sola identidad · Q tiempo → urgencias fibrilación 14 feb analítica 5 mar consulta dx HTA gr. 1 12 mar dx gr. 2 9 jun · hoy ingreso contenedor 14 jun paciente Todos los episodios cuelgan del mismo nodo Q; el orden lo da T; cada afirmación lleva su propia vigencia D6.
Figura 18.3. La historia clínica de Gonzales como serie temporal de episodios. La identidad de la paciente (maria_g, en Q) es una sola; cada urgencia, analítica, consulta o ingreso es una situación independiente en O que la referencia y se ordena por su eje T. La bitemporalidad (D6) permite leer la cinta «como ocurrió» o «como se creía» en cualquier fecha pasada.

La hospitalización: una situación-marco que contiene eventos

El control de hoy detectó algo que no tranquiliza: la presión no cede y la paciente tiene mareos. La doctora decide ingresarla unos días para vigilancia.

Aparece entonces una estructura que la consulta ya insinuaba y que el ingreso lleva al extremo: una situación-marco que contiene a otras.

Si la consulta era una carpeta de cinco piezas, la hospitalización es una carpeta de carpetas. Una caja con paredes de tiempo, dentro de la cual ocurren decenas de eventos menudos: admisión, asignación de cama, signos vitales cada turno, rondas médicas, cada dosis, la evolución diaria. Hasta que se cierra con el alta.

El instinto equivocado sería crear una tabla hospitalizaciones con columnas para «cama», «fecha de alta» y un campo de texto «notas». El modelo no necesita nada de eso.

El ingreso es una situación más en O, con su propio identificador, y todo lo que ocurre dentro le cuelga con el cable de siempre: parte_de. La hospitalización no es un registro. Es un contenedor.

tripletas
(ingreso_9120) ∈ O
  instancia_de : hospitalizacion           # K · qué clase de marco es
  paciente     : maria_g                       # Q · a quién se ingresa
  agente       : dra_navarro                # Q · quién lo indica
  servicio     : cardiologia                # K · bajo qué servicio
  cama         : cama_4b_307                # O · la cama asignada (objeto físico)
  inicio       : 2026-06-14T11:20-05:00     # T · cuándo se admite
  estatus      : en_curso                   # K · admitido | en_curso | de_alta

# Todo lo que ocurre adentro cuelga del marco con parte_de
(admision_9120,    ∈ O,  parte_de,  ingreso_9120)   # T: 14 jun 11:20
(signos_9120_t1,   ∈ O,  parte_de,  ingreso_9120)   # T: 14 jun, ronda mañana
(ronda_9120_d1,    ∈ O,  parte_de,  ingreso_9120)   # T: 14 jun, visita médica
(adm_farmaco_9120, ∈ O,  parte_de,  ingreso_9120)   # T: 14 jun, dosis administrada
(evolucion_9120_d2,∈ O,  parte_de,  ingreso_9120)   # T: 15 jun, nota de evolución
(alta_9120,        ∈ O,  parte_de,  ingreso_9120)   # T: 17 jun, cierre del marco

Fíjate en la cama. cama_4b_307 es un objeto físico en O, no una etiqueta: tiene ubicación, ocupación, mantenimiento.

Que el ingreso la señale por L/O en vez de copiarla como texto es lo que permite responder «¿qué camas están libres en cardiología ahora mismo?» sin inventar otra tabla.

El servicio (cardiologia) es otra cosa: una clase en K. Nadie pregunta por su identidad; se usa como categoría.

El premio de modelar el ingreso como marco es que cada evento interior conserva su contexto sin repetirlo. La dosis del 14 de junio no arrastra «paciente Gonzales, servicio cardiología, cama 307».

Todo eso se hereda subiendo por parte_de hasta el marco. La trazabilidad es estructural, no copiada.

ingreso_9120 · la hospitalización (marco · O) 14 jun admisión 17 jun alta admisión cama 4B-307 signos por turno ronda visita médica medicación administrada evolución nota diaria parte_de M(O→O) Las paredes son el eje T (admisión → alta); el interior son eventos en O; el cable que los cuelga del marco es M.
Figura 18.4. La hospitalización como situación-marco. Las paredes verticales son el eje T (la admisión abre la caja, el alta la cierra); dentro ocurren, en orden, la asignación de cama, los signos vitales, las rondas, la medicación administrada y las notas de evolución. Todos cuelgan del marco con parte_de y heredan su contexto sin repetirlo. Un contenedor, no un registro.

El enlace con la farmacia interna: prescripción, dispensación, administración

Hay un eslabón que el software hospitalario de juguete casi siempre rompe: lo que la doctora receta no es lo que el paciente recibe.

Entre la receta y el cuerpo de Gonzales hay una cadena de custodia que cruza el módulo clínico con el de farmacia, igual que en un ERP el de ventas conversa con el de inventario.

Son tres situaciones encadenadas, cada una con su autor y su momento: la prescripción, donde la doctora ordena; la dispensación, donde la farmacia entrega un lote concreto; y la administración, donde la enfermería se lo aplica al paciente.

El detalle que lo cambia todo es el lote. La farmacia no entrega «el antihipertensivo», que es un concepto en K. Entrega una caja física con su vencimiento y su rastro, igual que en minería un saco de mineral arrastra su origen.

Si mañana retiran ese lote por un defecto de fabricación, el grafo tiene que responder en segundos: «¿a qué pacientes se les administró algo de este lote?».

Esa pregunta solo tiene respuesta si el lote es un objeto en O y no un texto en un comentario.

tripletas
# El lote es un OBJETO físico en O, con su vencimiento y su clase de fármaco
(lote_av2207) ∈ O
  instancia_de : lote_farmaceutico        # K · qué clase de objeto es
  es_de        : antihipertensivo         # K · de qué fármaco (concepto)
  vence        : 2027-09-30                # T · caducidad del lote
  custodia     : farmacia_central         # Q · quién lo guarda

# La cadena de custodia: tres situaciones encadenadas, cada una con su momento
(prescrip_8842) ∈ O    # ya existía: la doctora ordena (consulta de hoy)
(dispensa_9133) ∈ O
  instancia_de : dispensacion             # K
  cumple       : prescrip_8842            # M · qué orden satisface
  agente       : farmacia_central         # Q · quién dispensa
  lote_entregado: lote_av2207             # O · qué lote concreto sale
  cantidad     : 14                        # N · unidades entregadas
  inicio       : 2026-06-14T12:05-05:00    # T
(adm_farmaco_9120) ∈ O
  instancia_de : administracion_farmaco   # K
  cumple       : dispensa_9133            # M · qué dispensación consume
  agente       : enf_rojas                # Q · quién la aplica
  paciente     : maria_g                     # Q · a quién
  lote_usado   : lote_av2207              # O · trazabilidad del lote hasta el cuerpo
  parte_de     : ingreso_9120             # M · ocurre dentro de la hospitalización
  inicio       : 2026-06-14T20:00-05:00    # T

La cadena se lee de un tirón siguiendo los cables cumple: la administración consume una dispensación, que a su vez satisface una prescripción. No hay tablas que cruzar ni claves que adivinar.

Y como los tres eslabones apuntan al mismo lote_av2207, la pregunta del retiro se contesta de una sola pasada.

python
# Retiro de lote: ¿a qué pacientes llegó algo de lote_av2207?
afectados = u.subjects_where("lote_usado", lote_av2207, role="paciente")
# → [maria_g, ...]   en segundos, cruzando clínica y farmacia sin un solo JOIN
adm_farmaco_9120O cumpleM(O→O) dispensa_9133O cumpleM(O→O) prescrip_8842O

Cross-módulo sin pegamento

Clínica y farmacia son «módulos» distintos solo en la cabeza del organigrama. En el grafo son el mismo tejido: la receta vive en el módulo clínico, el lote en el de farmacia, y el cable cumple los une sin que nadie escriba código de integración. La trazabilidad del lote es a la farmacia lo que la del mineral será a la minera.

prescripción prescrip_8842 dra_navarro · ordena dispensación dispensa_9133 farmacia · entrega administración adm_farmaco_9120 enf_rojas · aplica cumple cumple lote_av2207 · vence 2027-09 · trazabilidad el mismo objeto físico atraviesa dispensación y administración Tres situaciones en O encadenadas por M (cumple); el lote, objeto en O clasificado por K, las cruza para la trazabilidad.
Figura 18.5. La cadena de la farmacia interna. La administración de la enfermería cumple una dispensación de la farmacia, que a su vez cumple la prescripción de la doctora (tres situaciones en O encadenadas por cumple). El lote concreto (lote_av2207) atraviesa la dispensación y la administración, de modo que un retiro de lote se resuelve con una sola proyección, sin cruzar tablas entre el módulo clínico y el de farmacia.

Del paciente a la población

Todo este capítulo diseccionó a una paciente. Gonzales tiene nombre, una fibrilación en febrero, un diagnóstico que empeoró en marzo.

Pero quien dirige el hospital rara vez pregunta por Gonzales. Pregunta por la población que ella integra. Cuántos pacientes arrastran hoy una hipertensión grado 2. Qué diagnóstico encabezó el trimestre. Cuántas camas sigue ocupando cardiología esta mañana.

Y ahí está el premio que no se ve mientras modelas: ninguna de esas preguntas pide una tabla nueva. El diagnóstico de Gonzales es una situación más, del mismo tipo que los de los otros mil pacientes, y contar los de un tipo es contarlos todos a la vez.

Diseccionar con cuidado una sola consulta dejó armado, sin buscarlo, el tablero epidemiológico.

python
# Cuántos diagnósticos de hipertensión grado 2 hay vigentes — la cohorte que Gonzales integra
count(u, Pattern(fixed={"diagnostico_asignado": u.ind("hta_grado_2")},
                 type_constraint=u.ind("diagnostico")))

El lexicon de la clínica

Para que la doctora no tenga que aprender jerga de bases de datos, cargamos el lexicon con las palabras exactas de la medicina.

El lexicon es el traductor del que habló la Parte IV: pasa el vocabulario humano a los roles del catálogo y decide qué significa cada palabra según su compañía. El verbo «controlar», por ejemplo, quiere decir cosas distintas según lo que lo acompañe.

python
lex.register(LexiconEntry(
    verb="controlar",
    situation_type="consulta_cardiologia",
    obligatory=["agente", "paciente"],
    pattern=("a_un_paciente",),        # "controlar a un paciente" → seguimiento clínico
))

lex.register(LexiconEntry(
    verb="controlar",
    situation_type="medicion_signo_vital",
    obligatory=["agente", "medida_evaluada"],
    pattern=("un_signo",),             # "controlar la presión" → una medición
))

Y le instalamos el dialecto de la casa: un mapa entre las palabras que el personal clínico usa de verdad y las etiquetas del catálogo.

El usuario final nunca toca esas etiquetas. El sistema traduce por debajo, igual que el spa traducía «IGV» a su clase de impuesto.

python
lex.register_domain_dialect("cardiologia_central", {
    "presión":        "presion_arterial",
    "el paciente":    "paciente",
    "receta":         "prescripcion_medica",
    "control":        "consulta_cardiologia",
    "alergia":        "contraindicacion_clinica",
    "la pastilla":    "antihipertensivo",
})

Gracias a eso, la doctora puede dictar «controlé al paciente, la presión salió alta, le subí la pastilla y lo cito en un mes», y el grafo lo traduce a sus identificadores sin que ella sepa que existen. La interfaz es su idioma.

El antes y el después: del expediente fragmentado al grafo único

Antes, en SQL. En un sistema hospitalario típico, el expediente de Gonzales vive repartido en cinco o seis tablas que no se hablan solas: pacientes, consultas, diagnosticos, prescripciones, contraindicaciones y, si el equipo fue cuidadoso, una diagnostico_historial para guardar los cambios.

¿La doctora quiere saber si el ajuste que acaba de recetar choca con una contraindicación vigente? Hay que cruzar prescripciones con contraindicaciones. ¿Y si la hipertensión sigue activa? Otro JOIN contra diagnostico_historial, filtrando por fechas.

La respuesta no está en ninguna tabla. Vive en el pegamento que el programador escribe y reescribe cada vez que cambia la lógica clínica.

sql
-- Antes: el diagnóstico vigente vive en otra tabla, versionado por fechas;
-- la pregunta clínica obliga a encadenar JOINs y filtrar por rango temporal.
CREATE TABLE diagnosticos          (id INTEGER PRIMARY KEY, paciente_id INT, codigo TEXT);
CREATE TABLE diagnostico_historial (id INTEGER PRIMARY KEY, diag_id INT,
                                    grado TEXT, valido_desde DATE, valido_hasta DATE);
CREATE TABLE prescripciones        (id INTEGER PRIMARY KEY, paciente_id INT, farmaco_id INT);

SELECT h.grado
FROM diagnostico_historial h
JOIN diagnosticos d ON d.id = h.diag_id
WHERE d.paciente_id = :paciente
  AND h.valido_desde <= :fecha            -- la lógica temporal,
  AND (h.valido_hasta IS NULL OR h.valido_hasta > :fecha);  -- a mano, cada vez

Después, en WQuestions. La misma información es un solo grafo de hechos mínimos. diag_hta_8842 cuelga de consulta_8842 con parte_de y lleva sus propias fechas de validez. prescrip_8842 apunta a regla_renal_alerta con verificado_contra.

La pregunta se contesta mirando el grafo con un filtro de fecha: el motor devuelve solo los hechos cuya ventana de validez cubre el día consultado.

No hay JOIN ni código de pegamento. La vigencia y las causas ya venían tejidas desde el primer hecho.

python
# Después: el diagnóstico vigente a una fecha es una proyección con filtro temporal.
vigente = u.value_at(maria_g, "diagnostico_asignado", at="2026-04-15")  # → hta_grado_1

En medicina, un diagnóstico no es un campo de texto: es una creencia fechada, con su evidencia detrás y su caducidad delante. Tratarlo como una entidad, y no como una columna que se sobrescribe, es lo que convierte un expediente en algo que un juez puede leer.La lección de la clínica

Cuánto pesó el dominio entero

Vale la pena medir la huella, porque desmiente el prejuicio de que tanta expresividad cuesta un diluvio de datos.

Toda la consulta de Gonzales cabe en un puñado modesto de hechos: las cinco piezas, los tres porqués de la receta, los dos diagnósticos con su historia, la contraindicación y el control previsto. Un esquema relacional necesita bastante más para sostener la misma memoria.

Figura 18.6. Huella de la consulta por bloque, en hechos atómicos. La pieza más cara es el diagnóstico, justamente porque carga la vigencia (D6): guardar dos verdades históricas en vez de pisar una sola cuesta unos hechos de más —y vale cada uno—. El total ronda las seis decenas de tripletas: un volumen ridículo frente a la maraña de tablas, historiales y procedimientos que el mismo caso exige en SQL.

La pepita metodológica: el arte de saber qué reificar

Llegamos a la tercera pregunta, la que dejamos pendiente al abrir el capítulo.

Al diseñar un dominio nuevo, ¿cómo decides qué dato merece volverse una entidad con identidad propia en O, y cuál se queda como un número callado en N o una etiqueta quieta en K?

La regla, en una línea

Si crees que en el futuro alguien va a hacer una pregunta o una afirmación sobre ese dato, reifícalo. Si nadie va a interrogarlo nunca, déjalo como valor.

Es una regla simple y brutal, y se aplica caso por caso. Mira cómo decide cada pieza de esta consulta su propio destino:

¿Reificamos el síntoma? Sí. Mañana el médico querrá saber cuándo empezó, qué tan intenso era y si mejoró. Se vuelve entidad en O.
¿Reificamos la medición de presión? Sí. Mañana la conectaremos como evidencia de un diagnóstico. Entidad en O.
¿Reificamos la contraindicación? Sí. Tiene autor, fecha de vigencia y condición disparadora; alguien la consultará. Entidad en O.

¿Reificamos «cada mañana»? No. Es un valor terminal. Nadie va a interrogar al concepto «cada mañana». Se queda como etiqueta en K.
¿Reificamos «20 mg»? No. Es una cantidad con su unidad. Vive en N y ahí muere.
¿Reificamos «148/95»? El número, no; pero la medición que lo contiene, sí (porque la medición sí será interrogada).

Convertir algo en entidad cuesta unos pocos bytes. No hacerlo cuesta mucho más: años después un regulador pide el historial completo de un dato que guardaste como texto plano, y toca rehacer medio sistema.

En medicina, además, se paga en algo peor que dinero. Por eso, ante la duda, la regla se inclina siempre a favor de la entidad.

Balance: un dominio nuevo, casi sin fricción

Cerremos repasando qué demostró el prototipo al absorber, desde cero, un dominio para el que la arquitectura no había sido diseñada.

El veredicto de la clínica

  • La carpeta maestra funcionó. La consulta abrazó sus cinco sub-situaciones con parte_de, igual que la venta abrazó sus líneas. Ni una tabla paralela, ni un esquema nuevo.
  • La única fricción fue sana. El error de firma al mezclar O con K no fue un fallo del modelo: fue el modelo corrigiendo al modelador. Se resolvió declarando un puñado de cables de dominio.
  • El diagnóstico se modeló completo. Su duda (modalidad), su evidencia (motivado_por) y su caducidad (D6) se guardaron en dimensiones separadas, sin aplastarse en un campo de texto.
  • La historia se preserva (D6). Dos diagnósticos contrarios conviven sin que se pierda el rastro forense. El sistema puede responder qué se sabía en cualquier fecha pasada.
  • El método quedó explícito. Evento ancla, piezas que nacen dentro, y la regla de qué reificar. Esa receta de elicitación sirve para el próximo dominio, sea cual sea.

La clínica parecía pedir una arquitectura propia y cupo en la misma de siempre, con un par de cables nuevos y ninguna excepción estructural.

Pero aquí asomó una exigencia que el próximo capítulo convierte en ley: que varios custodios, en hospitales distintos, puedan leer y escribir sobre el mismo paciente sin pisarse.

Subimos al dominio más implacable de todos. Un banco, donde no borrar el pasado deja de ser buen gusto y pasa a ser una orden del regulador.