Saltar al contenido

Arquitectura del repositorio

Principio

Una sola fuente de verdad por cada cosa, y todo lo demás generado a partir de ella. La alternativa —mantener a mano el currículo, los índices, los README y el sitio— produce la incoherencia clásica: el índice dice 60 clases, el README dice 58 y el sitio publica 55.

flowchart TD
    CY["curriculum.yaml<br/>partes · clases · horas · fuentes<br/>resumen · prerrequisitos · pauta"] --> BC["scripts/build_classes.py"]
    LM["classes/**/lesson.md<br/>la materia, escrita a mano"] --> BC
    MY["classes/**/motores.yaml<br/>el caso y la matriz de motores"] --> BC
    IM["classes/**/implementaciones/<br/>el código real, uno por motor"] --> BC
    SJ["catalog/sources.json<br/>120 fuentes"] --> BC
    GJ["catalog/glosario.json<br/>306 términos"] --> BC
    BC --> CR["classes/**/README.md<br/>+ portadas de parte<br/>+ GLOSARIO.md<br/>(generados)"]

    MY --> VE["scripts/verificar_equivalencia.py"]
    IM --> VE
    VE --> MO{"¿todos los motores<br/>dan la misma respuesta?"}

    CY --> GS["scripts/generate_site.py"]
    CR --> GS
    SJ --> GS
    GJ --> GS
    DJ["catalog/databases.json<br/>27 motores"] --> GS
    GS --> ST["site/<br/>una página por clase + busqueda.json<br/>(generado)"]

    CY --> VR["scripts/validate_repository.py"]
    SJ --> VR
    GJ --> VR
    DJ --> VR
    LM --> VR
    MY --> VR
    RD["reference-data/"] --> VR
    VR --> OK{"¿main en verde?"}

Qué es fuente y qué es artefacto

Ruta Naturaleza Se edita a mano
curriculum.yaml Fuente
classes/**/lesson.md Fuente
classes/**/motores.yaml Fuente
classes/**/implementaciones/** Fuente
catalog/*.json Fuente
labs/, reference-data/, docs/ Fuente
classes/**/README.md Artefacto No
GLOSARIO.md Artefacto No
classes/README.md y classes/part-*/README.md Artefacto No
site/** Artefacto No

La integración continua ejecuta los generadores con --check y falla si algún artefacto quedó desactualizado. Es lo que impide que el sitio publicado y el repositorio digan cosas distintas.

La excepción: el «About» de GitHub

Hay una superficie que afirma cifras del programa y no vive en el árbol de ficheros: la descripción del repositorio en GitHub. Ningún grep la alcanza, ninguna revisión de un cambio la mira y la validación no la ve, pero es lo primero que lee quien llega. Estuvo anunciando las cifras de la versión anterior —clases, partes, horas y fuentes— mucho después de que el README dijera las correctas, sin que nada lo detectara.

Se comprueba y se corrige a mano, y conviene hacerlo cada vez que cambia una cifra de la portada:

gh api repos/<owner>/<repo> --jq '{description, homepage, topics}'

Para escribirla, el texto va en un JSON y se envía con gh api --method PATCH repos/<owner>/<repo> --input payload.json. Nunca por el shell: los emoji y las tildes se corrompen en tránsito y se acaba publicando mojibake justo al arreglar la coherencia. Se verifica releyendo de la API, con el texto escapado a ASCII, no por lo que imprima la consola.

Los componentes

curriculum.yaml

Metadatos de las 74 clases: identificador, slug, título, horas, nivel, conceptos, motores, laboratorio y fuentes. Lleva además la pauta pedagógica: el resumen y los prerrequisitos de cada clase, y por cada parte su introduccion, sus resultados y sus errores frecuentes. De ahí salen la portada de cada parte y el índice general. También contiene las rutas por objetivo y los pesos de evaluación. Nada de lo que aquí se declara se repite en otro sitio.

catalog/glosario.json

Los 306 conceptos que declaran las clases, definidos una sola vez. Cada entrada lleva la definición, la clase donde el término se introduce, la fuente que la respalda y los términos relacionados. De aquí salen tres cosas: el vocabulario que abre cada clase, el vocabulario de cada portada de parte y el GLOSARIO.md del programa.

El validador exige cobertura exacta en los dos sentidos: ningún concepto del currículo sin definición, y ninguna definición que ninguna clase declare. La primera regla evita que el lector tenga que deducir del contexto; la segunda evita que el registro acumule entradas que nadie revisa.

catalog/sources.json

Registro único de la bibliografía. Cada entrada lleva tipo, autoría, año, URL y una nota que explica para qué sirve en este programa; los libros llevan ISBN y los artículos, DOI o sede. Ver la política de fuentes.

catalog/databases.json

Los motores cubiertos, con su familia, lenguaje de consulta, documentación oficial y si tienen laboratorio ejecutable (core_lab) o solo ficha comparativa. La distinción evita prometer dominio de tecnologías que solo se mencionan.

classes/

Una carpeta por clase con cuatro cosas, y solo la última es artefacto:

Archivo Qué es
lesson.md La materia, escrita a mano
motores.yaml El caso con su salida esperada y la matriz de motores, con el porqué sí y el porqué no de cada uno
implementaciones/<motor>/ El código real que resuelve el caso en ese motor
README.md El documento publicable, generado a partir de los tres anteriores

La separación permite que las 74 clases compartan encabezado, rúbrica y formato de bibliografía sin 74 copias que mantener, y que el código de cada motor viva en un archivo que se puede ejecutar tal cual, no dentro de un bloque de Markdown.

scripts/verificar_equivalencia.py

El componente que sostiene la afirmación central del programa: el mismo problema, resuelto en varios motores, da la misma respuesta. Ejecuta cada implementación contra su motor y compara el resultado con el contrato de la clase. Tres niveles, declarados siempre:

Nivel Motores Dónde corre
nucleo SQLite, DuckDB En cualquier máquina y en todos los trabajos de CI
servicio PostgreSQL, MySQL, MongoDB, Redis, Neo4j Contra el contenedor real, con el cliente oficial de cada motor
declarado El resto del catálogo No se ejecuta, y así se dice

Cada clase corre en su propio espacio de nombres —un esquema en PostgreSQL, una base en MySQL, una base en MongoDB—, porque sin ese aislamiento las tablas de una clase impiden borrar las de otra y el verificador acabaría midiendo la basura que dejó la anterior.

labs/

Experimentos reproducibles. El primero usa SQLite en memoria y no depende de nada externo; el resto llega por perfiles de docker-compose.yml.

reference-data/

El dominio educativo canónico —estudiantes, cursos, inscripciones— que usan todas las clases. Que el dominio sea el mismo en las 74 clases es lo que permite comparar motores sin cambiar de problema.

site/

Sitio estático de GitHub Pages: portada con búsqueda y filtros, una página por clase con diagramas Mermaid, índices por parte, bibliografía renderizada y catálogo de motores.

Controles de integridad

Control Qué impide
Mínimo de dos fuentes por clase Material sin respaldo
Citas existentes en el registro Bibliografía fantasma
Sin fuentes huérfanas Registro que envejece sin revisar
ISBN en libros, DOI o sede en artículos Citas no localizables
Motores citados presentes en el catálogo Prometer cobertura inexistente
Secuencia 001..074 sin huecos Clases perdidas al reordenar
Cobertura exacta del glosario en los dos sentidos Términos usados sin definir y definiciones huérfanas
Remisiones del glosario resueltas «Ver también» que lleva a la nada
Resumen y prerrequisitos en toda clase Clases que aparecen sin explicar por qué están ahí
Prerrequisitos siempre anteriores Dependencias circulares en el orden de estudio
Introducción, resultados y errores en toda parte Portadas de parte que son solo una tabla
Todo motor con su porque_no Comparativas de folleto, con solo ventajas
Todo motor con su doc: en el dominio oficial Afirmaciones sobre un motor sin respaldo
Implementación declarada que existe en disco Código prometido y ausente
Resultado idéntico entre motores «Es equivalente» dicho sin comprobarlo
Secciones obligatorias en cada lección Notas disfrazadas de clase
Enlaces relativos resueltos Navegación rota
Codificación UTF-8 sin mojibake Acentos corrompidos al editar
Integridad del conjunto de referencia Laboratorios sobre datos inconsistentes
Artefactos regenerados Sitio y repositorio en desacuerdo

Decisiones y su motivo

Fuente de esta página: docs/ARCHITECTURE.md