Ir al contenido
Nicolás Amorosi

Villa Aurora, Florencia

Libretto

Villa Aurora llevaba sus registros académicos en una aplicación que había crecido durante veinte años. Diseñé y construí su reemplazo —modelo de datos, API, interfaz, seguridad y la migración de cada registro— y en octubre de 2026 pasó a ser la fuente de verdad de la facultad.

El problema

La aplicación anterior se apoyaba en una base de datos que había crecido tabla por tabla durante dos décadas: una tabla por carrera, booleanos guardados como −1 y 0, y texto en varias codificaciones a la vez. Los caracteres que no podía decodificar los sobrescribía, así que los acentos de nombres y materias se perdían para siempre.

La secretaría necesitaba un sistema confiable: notas en la escala italiana de 18 a 30, créditos (CFU) que cierren por plan de estudios, cohortes que avanzan año a año, y docentes y estudiantes viendo exactamente su parte.

Restricciones

  • Primero italiano, después inglés; y no solo la interfaz: los catálogos mismos son bilingües.
  • WCAG AA, con pantallas que funcionan en un teléfono de 320 píxeles de ancho.
  • Una sola máquina virtual con Docker Compose; sin plataforma administrada.
  • Los datos del sistema viejo tenían que seguir llegando hasta el corte, y debía poder correrlo una persona no técnica con un solo comando.
  • Construido casi en su totalidad por un desarrollador, así que las decisiones, trampas y procedimientos viven en el repositorio: 146 documentos.

Qué construí

Un monorepo con Turborepo: una API en NestJS 11 con Prisma sobre MySQL 8, una interfaz en Next.js 16 y React 19, y un paquete compartido de contratos TypeScript para que ambos lados coincidan en cada request y response. El navegador nunca llama a la API directamente: delante hay route handlers de Next.js.

La API responde con códigos de error estables en lugar de frases, y la interfaz los traduce. El acceso es un registro de permisos, cada uno con un alcance —propio, asignado o todo— que el servidor resuelve en cada request. Un administrador puede ver el sistema como otro usuario para reproducir exactamente lo que esa persona ve, y cada cambio queda en un log de auditoría, con los campos sensibles ocultos, en la misma transacción que el cambio.

Alrededor de ese núcleo: importación masiva desde Excel, generación de PDF, archivos en S3 con URLs prefirmadas y el tipo real del archivo verificado por sus bytes, recordatorios programados por email con Microsoft Graph, y tablas de historial que guardan cada versión anterior de los registros importantes.

Lo difícil

Ver notas como

Filtro que agrega el servidor

where: { courseId: { in: me.courseIds } }

Greco IStoria della ChiesaEbraico IEtica
Estudiante A28No se envía27No se envía
Estudiante B24No se envía30LNo se envía
Estudiante C30No se envía28No se envía
Estudiante D22No se envía26No se envía
Estudiante E29No se envía27No se envía
Estudiante F26No se envía30No se envía
Un mismo pedido de notas, respondido para tres personas. El alcance se convierte en un filtro de base de datos; las notas fuera de él nunca salen del servidor.

Alcances, no roles

Un docente puede ver notas, pero solo de las materias que tiene asignadas. Un estudiante, solo las suyas. La secretaría, todas. En vez de programar roles fijos, cada permiso lleva un alcance y la API lo convierte en un filtro de base de datos en cada request. La interfaz oculta lo que no podés hacer; quien lo impide es el servidor.

Sin tokens en el storage

El access token vive solo en memoria y expira rápido. El refresh token está en una cookie HttpOnly, se guarda hasheado y rota en cada renovación: el anterior se revoca cuando se emite el nuevo. Además, cada request que modifica datos lleva un token CSRF de doble envío.

Veinte años de datos

Nada de esto importaba hasta que cada nombre, materia y nota de la base vieja llegara intacto. Esa historia tiene su propia página.

Leer: Ni un carácter perdido

Resultado

  • En producción. El 13 de octubre de 2026 se congeló el sistema viejo y Libretto pasó a ser la fuente de verdad de la facultad.
  • 438 usuarios y 580 inscripciones en producción.
  • Conciliado contra las cifras del propio sistema viejo: las carreras con créditos que no cerraban bajaron de 141 a 3, y los promedios ponderados que no coincidían, de 32 a 5.
  • Alrededor de 1.200 tests automatizados (Jest y Playwright).

El código pertenece al cliente y es privado. Las figuras son ilustraciones con datos de ejemplo; en esta página no aparece ningún registro real.