Parte V · En la práctica
Una universidad
Aquí la dificultad no es el volumen ni la ley, sino la forma: el saber se ordena en un grafo de dependencias y una vida académica dura décadas. Veamos si el modelo sabe recorrer caminos.
Es lunes de matrícula y Tomás Quiroz tiene quince navegadores abiertos. Cursa el cuarto semestre de Ciencia de la Computación y quiere inscribirse en Algoritmos, el curso que todos dicen que «abre puertas». El portal de matrícula lo rechaza con un mensaje escueto: «No cumple prerrequisitos». Pero Tomás está seguro de haber aprobado lo que hacía falta. ¿Le falta Estructuras de Datos, que aprobó el ciclo pasado? ¿O es la rama de matemática, Cálculo, que arrastra desde primero? El sistema no se lo dice: solo sabe responder sí o no, nunca por dónde. Y detrás de ese silencio se esconde el problema técnico que define a una universidad como dominio.
Porque una universidad no se parece a un comercio ni a un banco. Sus dos rasgos propios son de otra naturaleza. El primero: los tiempos son largos. Una carrera dura cinco años, pero el historial de un egresado lo acompaña toda la vida: la nota de un curso que rindió a los veinte le sirve para un posgrado a los cuarenta. El segundo, y el que da título a este capítulo: las cosas dependen unas de otras. No puedes tomar Algoritmos sin antes haber pasado por Estructuras de Datos, y a esa no llegas sin Programación. La malla curricular es, literalmente, un grafo de dependencias.
Cualquier sistema académico serio tiene que poder responder, sin sudar, preguntas como estas:
- ¿Qué cursos puede tomar Tomás este semestre, dado lo que ya aprobó?
- ¿Qué nota sacó Lucía en Cálculo I antes de su reclamo? ¿Y después de la rectificación?
- ¿Quiénes seguían matriculados en Bases de Datos el 1 de mayo?
- ¿Qué cadena de prerrequisitos arrastra Aprendizaje de Máquina, y cuáles de ellos le faltan a Tomás?
- ¿Quiénes integraron el jurado de la tesis de Renata?
En una base relacional clásica, cada una de estas preguntas pide algo distinto: una tabla
histórica aquí, un WITH RECURSIVE allá, un procedimiento almacenado que replica
en código la lógica del recorrido. En WQuestions, como veremos, cada una es un
recorrido de uno o dos saltos sobre el mismo grafo de hechos. No hay máquina
distinta para cada pregunta: hay un solo mapa y muchas rutas sobre él.
La universidad sobre las siete coordenadas
Antes de los casos, repartamos las piezas del mundo académico entre los ejes de valor. El ejercicio ordena de un golpe lo que en un sistema heredado vive disperso en una veintena de tablas:
Q quién · agentes
Estudiantes, docentes, autoridades, miembros de jurado y la propia universidad como persona jurídica. Una sola persona puede aparecer en muchos roles: Renata es estudiante, jefa de práctica y tesista a la vez.
O qué · entidades y eventos
Carreras, planes, cursos, matrículas, evaluaciones, reclamos, tesis, defensas y graduaciones. Cada uno con identidad propia y trazabilidad. Aquí vive el grueso del peso académico.
L dónde · lugares
Facultades, aulas, laboratorios, auditorios y campus. Forman jerarquías como cualquier organización territorial: el aula es parte del pabellón, el pabellón del campus.
T cuándo · tiempos
Inicio y fin de cada ciclo, momento exacto de una matrícula, fecha de un examen, día de una defensa. El reloj académico es largo y no olvida: una nota de hace veinte años sigue siendo consultable.
N cuánto · magnitudes
Créditos, notas numéricas, horas semanales, montos de pensión. Cada magnitud arrastra su unidad: cuatro créditos no son cuatro puntos ni cuatro horas.
K cuál · clases
Tipos de carrera, estados de matrícula (vigente / retirado), modalidades de evaluación y niveles cualitativos (aprobado / desaprobado). El zócalo categórico que da sentido al resto.
Y el séptimo eje, M (el cómo), aporta los cables
que amarran todo: parte_de arma las jerarquías, requiere_prereq
teje la malla, agente ata cada persona a su situación. Sin esos enlaces, las
seis cajas anteriores serían seis listas inertes. Con ellos, una vida académica entera se
vuelve un grafo navegable. Recorrámoslo.
Caso 1 · La estructura académica como jerarquía
El plan de estudios de Tomás tiene una forma de árbol que el modelo absorbe sin esfuerzo.
La carrera contiene años; cada año, ciclos; cada ciclo, cursos. Una sola relación,
parte_de, expresa los cuatro niveles:
(carrera_cc) ∈ O # Ciencia de la Computación
(plan_2024) parte_de carrera_cc # la malla vigente desde 2024
(ciclo_2026_i) parte_de plan_2024 # el ciclo en curso
(cc_310_algoritmos) parte_de ciclo_2026_i # un curso del ciclo
(cc_310_algoritmos) creditos n_4_cr # arrastra su magnitud (N)
(cc_310_algoritmos) docente prof_salazar # apunta a un agente (Q)
(cc_310_algoritmos) dictado_en lab_b2 # y a un lugar (L)
Cada nivel es una entidad en O, y es parte de el nivel superior.
Tres tripletas de jerarquía y la columna vertebral de la carrera queda armada. Cuando Tomás
pregunta «¿qué cursos hay en mi ciclo actual?», la consulta es un único salto: trae todas
las entidades de O cuyo parte_de sea ciclo_2026_i. Y
cada curso, además de colgar de su ciclo, lleva sus propios atributos (créditos, docente,
aula, código, sílabo) sin que ninguno necesite una tabla aparte. Todo el inventario
académico tiene la misma forma.
Caso 2 · Los prerrequisitos como grafo dirigido acíclico
Llegamos al corazón del capítulo. La malla curricular (qué curso exige haber pasado por cuál) es por su propia naturaleza un grafo dirigido acíclico, un DAG: las aristas tienen sentido (de requisito a curso), no hay ciclos (ningún curso puede exigirse a sí mismo, ni directa ni indirectamente) y las dependencias se cruzan (un curso puede tener varios requisitos, y un requisito puede habilitar varios cursos).
DAG · grafo dirigido acíclico
Un conjunto de nodos unidos por flechas con sentido, sin que ninguna ruta te devuelva al punto de partida. Es la estructura natural de toda dependencia ordenada: las tareas de un proyecto, las versiones de un documento, las migraciones de una base de datos —y los prerrequisitos de una carrera—. Que no haya ciclos es justo lo que garantiza que siempre exista un orden válido para avanzar.
En un sistema relacional, esto se modela con una tabla curso_prereq(curso, prereq)
y se consulta con SQL recursivo. En WQuestions, cada prerrequisito es una sola
tripleta. Tomamos un fragmento real de la malla de Tomás (dos líneas que confluyen,
la de matemática y la de programación):
# línea de programación
(cc_210_estructuras) requiere_prereq cc_101_progra
(cc_220_basesdatos) requiere_prereq cc_210_estructuras
(cc_310_algoritmos) requiere_prereq cc_210_estructuras
# línea de matemática
(ma_120_calculo) requiere_prereq ma_110_discreta
(ma_230_probabilidad) requiere_prereq ma_120_calculo
# el curso que las une: necesita las DOS ramas a la vez
(cc_310_algoritmos) requiere_prereq ma_120_calculo
(cc_420_aprendizaje) requiere_prereq cc_310_algoritmos
(cc_420_aprendizaje) requiere_prereq ma_230_probabilidad
Algoritmos (cc_310) exige dos requisitos simultáneos
(uno de cada rama), y Aprendizaje de Máquina (cc_420), la cima de la
carrera, exige otros dos. El modelo lo absorbe sin pestañear porque
requiere_prereq es un cable múltiple: un mismo curso puede emitir varias flechas
de requisito, cada una su propio hecho atómico, independiente de las demás. No hay que
inventar una «tabla de dependencias compuestas»; basta apilar tripletas.
O; cada flecha es una tripleta
requiere_prereq (en color cómo). Las dos ramas (programación y
matemática) confluyen en Algoritmos, que requiere ambas a la vez, y la carrera
culmina en Aprendizaje de Máquina, que depende de Algoritmos y de
Probabilidad. Cada dependencia es un hecho atómico independiente.Ahora podemos resolver el drama del lunes. La pregunta «¿qué cursos puede tomar Tomás este
ciclo?» se vuelve un recorrido directo sobre el grafo. Para cada curso del ciclo, el motor
mira sus flechas requiere_prereq y verifica que todos los
requisitos tengan, en el historial de Tomás, una evaluación con estado aprobado. Si
falta uno solo, el curso queda excluido. Y (esto es lo que el portal del lunes no sabía
hacer) el mismo recorrido puede devolver el requisito que falta, no solo un «no».
El recorrido de elegibilidad, en una frase
Un curso es elegible para un estudiante si, y solo si, el conjunto de los
destinos de sus aristas requiere_prereq está contenido en el conjunto de
cursos que ese estudiante aprobó. Es una operación de conjuntos sobre el grafo: sin
join, sin SQL recursivo, sin procedimiento almacenado. La diferencia entre los dos
conjuntos es, además, la lista exacta de lo que le falta.
Apliquémoslo. Tomás aprobó Programación, Estructuras de Datos y
Discreta, pero no Cálculo. Algoritmos exige
cc_210_estructuras ✓ y ma_120_calculo ✗. El recorrido devuelve un
único faltante: Cálculo. El portal podría haberle dicho, en vez de un «no» mudo: «Te
falta Cálculo I para habilitar Algoritmos». El grafo conocía la respuesta desde el
principio; solo había que recorrerlo en lugar de comparar banderas.
Caso 3 · La matrícula que cambia de estado sin perder el pasado
Tomás se matricula en Bases de Datos el 10 de marzo. Lucía también, dos días
después. Pero a mediados de abril Lucía se retira del curso. ¿Cómo guardamos esto sin perder
información? La tentación clásica (y el error más común) es poner una columna
estado en la tabla de matrículas y actualizarla cuando alguien se
retira. El problema aparece en julio, cuando la secretaría pregunta: «¿quiénes estaban
matriculados el 1 de abril?». La respuesta fiel ya no existe: el UPDATE borró el
estado anterior.
En WQuestions los dos estados conviven en el grafo, separados por vigencia. No se sobrescribe nada: se cierra el intervalo del estado viejo y se abre el del nuevo.
D6 Vigencia: el pasado no se sobrescribe
Como vimos al modelar el banco, ningún atributo que pueda cambiar se
guarda como un valor desnudo, sino con un intervalo [inicio, fin). Cambiar el
estado de una matrícula no es modificar una celda: es cerrar el intervalo vigente y abrir
uno nuevo. La consecuencia es que el grafo conserva, gratis, toda la trayectoria de la
matrícula a lo largo del tiempo.
(matricula_lucia_bd, estado, vigente,
inicio=2026-03-12, fin=2026-04-18)
(matricula_lucia_bd, estado, retirada,
inicio=2026-04-18, fin=hoy)
La pregunta «¿estaba Lucía matriculada el 1 de abril?» se responde directo: una consulta
con al=2026-04-01 filtra por vigencia y devuelve vigente. La misma
consulta con al=2026-05-01 devuelve retirada. Sin tabla histórica
auxiliar, sin esfuerzo extra. La matrícula nunca se borra: cambia de estado y el historial
queda fechado para siempre.
Caso 4 · Una persona, muchos roles
Una verdad incómoda del mundo académico: las personas no son una sola cosa. Renata Ferro es
estudiante de quinto año y, al mismo tiempo, jefa de práctica del curso de
Programación. En unos años será docente titular; más adelante, directora de tesis; quizá,
decana. Es la misma persona (un solo individuo en Q), pero
ocupa roles distintos en situaciones distintas.
En un sistema tradicional esto se vuelve un dolor de cabeza: una tabla estudiantes,
otra docentes, claves cruzadas y un proceso de sincronización para el día en que
un estudiante se convierte en profesor. En WQuestions el problema no existe,
y la razón es una decisión de diseño que ya conoces:
D5 Agencia contextual
El rol de agente lo determina el verbo de la situación, no un «tipo» fijo
de la persona. Renata es renata_q: aparece como agente en una matrícula (rol
estudiante), como agente en una asignación docente (rol jefa de práctica),
como agente en una tesis (rol tesista). El sistema no distingue entre tipos de
personas; distingue entre tipos de situaciones. Cada rol vive en su situación.
# Renata como estudiante
(matricula_renata_ml, agente, renata_q)
(matricula_renata_ml, tema, cc_420_aprendizaje)
# Renata como jefa de práctica
(asignacion_jp_2026, agente, renata_q)
(asignacion_jp_2026, rol, rol_jefe_practica) # ← rol de dominio (K)
(asignacion_jp_2026, tema, cc_101_progra)
# Renata como tesista
(tesis_renata, agente, renata_q)
(tesis_renata, instancia_de, defensa_tesis)
Cuando alguien pregunta «¿qué hizo Renata este año?», la consulta es una sola: trae todas
las situaciones donde renata_q sea agente y proyecta su
instancia_de. La respuesta sale del grafo de forma natural y abarca lo
académico, lo docente y lo investigativo en una sola lista. No hubo que unir tres tablas de
tres «tipos» de persona, porque nunca hubo tres tipos: hubo una persona y tres situaciones.
Caso 5 · La cadena nota → reclamo → rectificación
Llegamos al caso emblemático de la trazabilidad académica. Lucía rinde el examen final de Cálculo I el 15 de julio. La Dra. Bravo corrige y le pone 12. Lucía revisa, no está de acuerdo con la corrección de un ejercicio y presenta un reclamo formal el 20 de julio. La docente lo revisa, le da la razón y el 25 de julio rectifica la nota a 15.
En un sistema tradicional, la nota se sobrescribe. El historial (si existe) vive en una tabla auxiliar que casi nadie consulta. Cuando años después alguien quiere reconstruir el incidente (un decano que atiende una queja, un comité de calidad que audita), el rastro está borroso. En WQuestions, las tres situaciones conviven en el grafo, y la causa que las encadena queda explícita.
Aquí entra en juego una decisión que el libro ya estableció: el «por qué» no es un eje, sino una relación entre hechos, repartida en cuatro cables distintos.
D7 El porqué, repartido en cuatro cables
No hay un eje «por qué»: la causalidad se expresa con
causado_por, motivado_por, con_finalidad y
justificado_por. La rectificación está motivado_por el reclamo
(esa fue su razón) y rectifica la evaluación original (ese es su blanco). La
graduación, más adelante, estará justificado_por la defensa. El audit trail no
es una tabla aparte: es la propia topología del grafo.
# 1. la evaluación inicial (situación reificada)
(eval_lucia_calc) ∈ O
instancia_de: evaluar_examen
agente: dra_bravo
paciente: lucia_q
tema: ma_120_calculo
momento: 2026-07-15
# 2. el reclamo: otra situación, agente = Lucía
(reclamo_lucia) ∈ O
instancia_de: reclamar_nota
agente: lucia_q
tema: eval_lucia_calc
momento: 2026-07-20
# 3. la rectificación: causa explícita y blanco explícito
(rectif_lucia) ∈ O
instancia_de: rectificar_nota
agente: dra_bravo
motivado_por: reclamo_lucia # ← por qué se hizo
rectifica: eval_lucia_calc # ← qué corrige
momento: 2026-07-25
¿Y la nota vigente? Cambia con la misma vigencia D6 del Caso 3. La calificación era 12 entre el 15 y el 25 de julio; desde el 25 es 15. Las dos versiones existen, fechadas:
(eval_lucia_calc, nota_vigente, n_12,
inicio=2026-07-15, fin=2026-07-25)
(eval_lucia_calc, nota_vigente, n_15,
inicio=2026-07-25, fin=hoy)
La consulta con al=2026-07-18 devuelve 12; con al=2026-07-30,
devuelve 15. El reclamo sigue en el grafo, consultable años después, y la cadena causal está
a la vista: la rectificación está motivado_por el reclamo y rectifica
la evaluación original. Si dentro de cinco años alguien revisa el caso de Lucía, el grafo le
entrega la historia completa sin gimnasia alguna.
La trampa del UPDATE en las notas
Corregir una nota tienta a ejecutar UPDATE notas SET valor=15
WHERE id=…. Es la decisión que destruye la trazabilidad: al actualizar la celda, se
borra la prueba de que alguna vez la nota fue 12 y de que un reclamo la cambió. El reflejo
más natural del programador es, también aquí, el peor enemigo de la institución. El modelo
lo previene: la nota anterior no se modifica, se le cierra el intervalo de vigencia y queda
en el grafo.
Caso 6 · Una defensa de tesis con director y jurado
El último escenario es la defensa de tesis de Renata. Una tesis es una situación de larga duración (un año, en su caso) con varios participantes que no juegan el mismo papel:
La tesista es Renata, la agente principal. El director es
el Dr. Acuña, un rol específico del dominio. Y el jurado lo componen tres
personas con funciones distintas: la Decana Ortiz como presidenta, la Dra. Bravo como vocal
y el Dr. Lima como secretario. En un sistema tradicional, todo esto pide una tabla
tesis_participantes con una columna discriminadora de tipo, y
cada consulta tiene que filtrar por ese código.
En WQuestions, cada rol es una tripleta separada. No hay tabla puente ni
columna discriminadora: el rol es el nombre del cable. Preguntar «¿quién presidió
el jurado de Renata?» es leer un solo enlace, jurado_presidente, sin filtrar
ninguna columna de tipo. La forma del dato coincide con la forma de la pregunta.
(tesis_renata, agente, renata_q)
(tesis_renata, director_tesis, dr_acuna)
(defensa_renata, parte_de, tesis_renata)
(defensa_renata, jurado_presidente, decana_ortiz)
(defensa_renata, jurado_vocal, dra_bravo)
(defensa_renata, jurado_secretario, dr_lima)
(defensa_renata, momento, 2026-12-04)
# la graduación: justificada por la defensa exitosa
(graduacion_renata, instancia_de, graduar)
(graduacion_renata, agente, renata_q)
(graduacion_renata, justificado_por, defensa_renata)
Roles como director_tesis, jurado_presidente,
jurado_vocal y jurado_secretario no figuran en el catálogo canónico
(ese catálogo invisible que vimos al hablar del lexicon). No importa: la política liberal del
modelo los acepta sin protestar, y mientras el dominio universitario los use de forma
consistente, el motor opera con ellos como si fueran canónicos. La graduación, por su parte,
queda justificado_por la defensa: Renata se graduó porque defendió con
éxito su tesis. El grafo deja la cadena causal escrita, sin un solo campo añadido al motor.
En una universidad, un expediente no es una foto del presente: es la película completa de una vida que el tiempo no debería poder reescribir.La lección del expediente académico
Del expediente al tablero del rectorado
Modelar la matrícula de Tomás y la malla que arrastra Aprendizaje de Máquina es
resolver un caso. Pero el rectorado vive en el agregado: cuántos estudiantes siguen
vigentes en cada carrera, qué curso acumula la tasa de desaprobación más alta, cuántos egresan
dentro de los cinco años. Ninguna de esas preguntas pide una tabla nueva. La inscripción de
cada alumno ya es una situación de tipo matricula con su carrera y su estado; la
estadística del semestre es contar esas situaciones, agrupadas por la coordenada que el
decano necesite.
# Matrículas vigentes en una carrera — un corte; el tablero recorre todas las carreras
count(u, Pattern(fixed={"carrera": u.ind("carrera_cc"), "estado": u.ind("vigente")},
type_constraint=u.ind("matricula")))
El antes y el después: del esquema fragmentado al grafo único
Pongamos las dos arquitecturas frente a frente con la pregunta que abrió el capítulo, ahora en su forma más exigente: ¿qué cadena de prerrequisitos arrastra «Aprendizaje de Máquina», cuáles de ellos ya aprobó Tomás y cuál le falta?
Antes, en el modelo relacional. La información se reparte entre
alumnos, cursos, prerequisitos, matriculas
y notas. Para expandir el árbol de dependencias de un curso hay que escribir un
WITH RECURSIVE sobre prerequisitos, y luego cruzarlo con
matriculas y notas mediante varios joins. Cada vez que la
malla cambia (y cambia cada pocos años), la consulta hay que revisarla con lupa.
-- Antes: el árbol de prerrequisitos exige SQL recursivo.
WITH RECURSIVE cadena(curso) AS (
SELECT prereq FROM prerequisitos WHERE curso = 'cc_420_aprendizaje'
UNION
SELECT p.prereq
FROM prerequisitos p
JOIN cadena c ON p.curso = c.curso
)
SELECT c.curso,
CASE WHEN n.estado = 'aprobado' THEN 'ok' ELSE 'FALTA' END
FROM cadena c
LEFT JOIN notas n
ON n.curso = c.curso AND n.alumno = 'tomas_q';
-- ...y todavía falta cruzar vigencias para la malla histórica.
Después, en WQuestions. La misma información vive como un solo grafo. Cada
prerrequisito es una tripleta requiere_prereq; cada curso aprobado es un nodo
alcanzable desde Tomás. La cadena completa de dependencias de Aprendizaje de Máquina
se obtiene con un recorrido hacia atrás sobre las flechas requiere_prereq (sin
SQL recursivo, sin procedimiento almacenado). Determinar qué le falta es restar dos
conjuntos: la cadena de requisitos menos su subgrafo de aprobados. Y el grafo no distingue
entre malla vigente y malla histórica: la bitemporalidad ya lo cubre, así que la misma
consulta sirve para el plan de hoy y para el de hace diez años.
Qué quedó probado en este capítulo
La universidad es, probablemente, el dominio donde el timeline largo y los grafos de dependencia dejan de ser un detalle accesorio y se vuelven el problema central. El modelo absorbió ambas cosas con su maquinaria estándar, sin un solo añadido al motor:
El veredicto del dominio de la forma
- Dependencias como grafo. La malla curricular es un DAG, y cada prerrequisito un hecho atómico. La pregunta «¿qué puedo tomar?» es un recorrido sobre el grafo que, de paso, devuelve lo que falta —no un sí o no mudo.
- Historial intacto de por vida. La vigencia (D6) deja
matrículas, notas y estados con su trayectoria completa durante toda la vida académica del
egresado. La trampa del
UPDATEnunca llega a ser una operación disponible. - Una persona, muchos roles. La agencia contextual (D5) permite que Renata sea estudiante, jefa de práctica y tesista a la vez: el grafo no distingue tipos de personas, sino tipos de situaciones.
- Cadenas causales explícitas. El porqué repartido en cables (D7) deja a la vista que una rectificación se hizo por un reclamo, o que una graduación se justifica por una defensa. El audit trail es la topología misma del grafo.
Y, una vez más, el motor no creció ni una línea para absorber el dominio.
Lo único que creció fue el lexicon: un puñado de verbos (matricular,
dictar, evaluar, reclamar, rectificar_nota,
graduar, defender_tesis) con sus signaturas, más un dialecto de
dominio que mapea «estudiante», «docente», «tesista» o «director de tesis» a sus roles. En el
próximo capítulo dejamos el campus y cruzamos a lo público: una municipalidad, donde las
fechas de vigencia de una norma deciden la vida de un barrio entero.