# ADR-002: Normalización de las rutinas de tarde (catálogo + asignación) | Metadata | Valor | |----------|-------| | **Estado** | Aceptado (implementado) | | **Fecha** | 2026-07-12 | | **Autor(es)** | Jaume Garriga Maestre | | **Supersede** | — | ## Contexto El modelo original de `AfternoonRoutine` mezclaba en una fila la **definición** de la rutina (labelEs/labelCa, emoji, color, monedas) y su **asignación** (niño, día, orden). Para tener "Merendar" de lunes a viernes había que crear la misma rutina cinco veces, y editar el emoji exigía tocar cinco filas. El usuario lo detectó como fricción real de uso. Las **mañanas ya estaban bien normalizadas**: `Activity` (catálogo por familia) + `WeeklyTemplateEntry` (asignación niño+día+orden+override de monedas). Las tardes eran la anomalía. ## Opciones Consideradas ### Opción A: Mantener el modelo y duplicar en la UI ("crear en varios días") | Pros | Contras | |------|---------| | Sin migración de datos | La duplicación persiste en BD: editar sigue costando N filas | | Cambio solo de frontend | La asimetría mañana/tarde se consolida | ### Opción B: Reutilizar `Activity` también para las tardes | Pros | Contras | |------|---------| | Una sola entidad de catálogo | Semántica forzada: Activity arrastra material del cole (N:M) que la rutina no necesita | | Menos código | Acopla dos conceptos de dominio distintos | ### Opción C: Nueva entidad `RoutineTask` gemela de `Activity` (elegida) | Pros | Contras | |------|---------| | Simetría total mañana/tarde: mismo patrón mental y de código | Migración de datos con deduplicación | | Definir una vez, asignar a N días y N niños | Una entidad y un CRUD más | | Monedas con cascada: override por día → catálogo → niño | | ## Decisión **Opción C.** `RoutineTask` = catálogo por familia (labels, emoji, color, monedas por defecto). `AfternoonRoutine` queda como pura asignación (`child + dayOfWeek + routineTask + orderIndex + coinsReward` como override). La migración Liquibase (changeset 003) consolida los duplicados existentes por clave natural (familia + labels + emoji + color) sin pérdida; el changeset 004 añade `ON DELETE CASCADE` para que borrar del catálogo arrastre las asignaciones (el modelo mental del padre). ```mermaid erDiagram FAMILY ||--o{ ROUTINE_TASK : "define una vez" ROUTINE_TASK ||--o{ AFTERNOON_ROUTINE : "se asigna a N dias/niños" CHILD ||--o{ AFTERNOON_ROUTINE : "" FAMILY ||--o{ ACTIVITY : "(patrón gemelo de mañanas)" ACTIVITY ||--o{ WEEKLY_TEMPLATE_ENTRY : "" CHILD ||--o{ WEEKLY_TEMPLATE_ENTRY : "" ``` ## Trade-offs ### Optimizamos para: - **Coste de edición del padre**: cambiar una rutina se hace UNA vez y se propaga. - **Consistencia del dominio**: un solo patrón (catálogo+asignación) para todo el día. ### Sacrificamos: - **Simplicidad de una tabla única**: aceptable; el patrón ya existía en mañanas. - **Riesgo puntual de migración**: mitigado con rollback explícito por changeset y verificación contra Postgres real (Testcontainers, `MigrationIT`). ### Asunciones que podrían invalidar esta decisión: - Si apareciera la necesidad de rutinas con material asociado (como Activity), reconsiderar la unificación con `Activity` (Opción B) en lugar de duplicar N:M. ## Consecuencias - (+) Alta multi-día en una llamada (`POST /routines` con lista de días). - (+) Cascada de monedas expresiva: día > catálogo > `coinsPerTask` del niño. - (−) Lección aprendida en la migración: H2 no detecta el tipado estricto de Postgres (`CAST(NULL AS INTEGER)`); por eso existe `MigrationIT` con Testcontainers. ## Criterios de Éxito - [x] Una rutina definida una vez y asignada a varios días/niños (tests verdes). - [x] Migración sin pérdida verificada contra Postgres 16 real. - [x] Borrar del catálogo arrastra asignaciones sin error 500 (test de cascade). ## Referencias - `backend/.../domain/RoutineTask.java`, `domain/AfternoonRoutine.java` - `backend/src/main/resources/db/changelog/changes/003-routine-catalog.yaml`, `004-routine-task-cascade.yaml` - Artefactos SDD en engram: `sdd/rutinas-reutilizables/{proposal,spec,design,tasks,verify-report}`