← Todos los proyectos

Forma

Un modelo al que no se le permite inventar datos

Estado Preproducción, y una banda en la app lo avisa.


El problema

Un plan semanal de entrenamiento y alimentación que vive en una hoja de cálculo no puede reaccionar a la composición corporal de la semana pasada, a una carrera que terminó a las once de la noche en un verano de Alicante, ni a un cambio en los precios de la compra.

Forma conecta composición corporal, entrenamiento, nutrición y coste de la compra semanal en un mismo ciclo, e intenta responder a una sola pregunta: qué debería hacer esta semana, visto lo que pasó la semana pasada.


El límite alrededor del modelo

Un modelo de lenguaje genera el plan. Nunca afirma un dato. La API publica un catálogo de alimentos, cada uno con macros que alguien midió, y un plan importado solo puede referenciar identificadores de ese catálogo. Cualquier otra cosa se rechaza al importar.

El razonamiento está escrito en el documento del formato en una frase: los macros de un alimento son un dato que alguien mide, no que un modelo estime. El modelo puede componer. No puede afirmar.


Un dominio que no sabe de frameworks

El backend es hexagonal, con el dominio libre de tipos de framework y la infraestructura contenida en la frontera de los adaptadores. La persistencia es JDBC sobre PostgreSQL con migraciones Flyway y deliberadamente sin ORM, una decisión registrada en un architecture decision record en lugar de dejarla para que alguien la redescubra más tarde.

Sesenta y pico migraciones después, esa decisión sigue aguantando, que es la única evidencia que importa sobre una decisión de arquitectura.


Medido, no tecleado

Las mediciones corporales entran por una integración OAuth con Withings en lugar de a mano, porque un número que tienes que reteclear es un número que dejas de registrar. Un servicio de insights semanales convierte esas métricas en bruto en ajustes pequeños, del orden de cien calorías, en vez de reescrituras dramáticas del plan.


Cómo está construido

Trece architecture decision records, una especificación por historia guardada en el repositorio, y un frontend React 19 cubierto por tests unitarios en Vitest y ejecuciones end-to-end con Playwright, todo verificado en integración continua.


Stack

Java 21Spring Boot 3HexagonalPostgreSQL 17FlywayReact 19TypeScriptViteVitestPlaywrightDocker Compose

Escrito en el laboratorio