diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 2ab9c38..9ca32dd 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -77,11 +77,15 @@ Principios estructurales (de ADR-001): - **Umbral de k-anonimato** obligatorio en toda visualización compartida (nunca mostrar celda con n < umbral). -## 6. ADRs iniciales +## 6. ADRs + +Ver `docs/adr/` para los archivos completos. - **ADR-001** — Modelo de privacidad y gobierno de datos *(el corazón del producto)*. - **ADR-002** — Stack tecnológico y residencia de datos. - **ADR-003** — Consentimiento granular versionado e inmutable. +- **ADR-004** — Capa de identidad y autenticación (SSO Keycloak para staff; sin login para respondentes). +- **ADR-005** — Esquema declarativo de formularios y estrategia de dos fases (Sprint 4). La lógica del flujo vive en datos (JSONB), interpretada por un motor backend; el frontend wizard es un renderer sin reglas de negocio. Motor genérico diferido a Fase 2. ## 7. Riesgos diff --git a/BACKLOG.md b/BACKLOG.md index 72491ee..12d8e0a 100644 --- a/BACKLOG.md +++ b/BACKLOG.md @@ -58,6 +58,17 @@ --- +### [BACKLOG-002] Fase 2 — Motor genérico de formularios +- **Estado:** Diferido post-deadline +- **Contexto:** En Sprint 4 se construye un intérprete del esquema declarativo específico + para el formulario de cuidadores (ADR-005, Fase 1). Una vez entregado ese caso, el + intérprete se extrae a un motor universal que interprete cualquier esquema definido en + `questions.conditional_rules` + campos de instanciación, sin hardcodear ningún formulario. + El formulario de cuidadores se vuelve el primer caso de prueba del motor sin reescribir + el contrato de datos. +- **Dependencia:** Fase 1 completada (Sprint 4 — wizard funcional). +- **Ver:** ADR-005 (`docs/adr/ADR-005-esquema-declarativo-formularios.md`). + - Dashboard / reportes (Sprint 3) - Formulario de consentimiento + persistencia de respuestas (Etapa 2B) - Builder visual de encuestas (post-validación del caso fijo) diff --git a/docs/adr/ADR-005-esquema-declarativo-formularios.md b/docs/adr/ADR-005-esquema-declarativo-formularios.md new file mode 100644 index 0000000..d4dc283 --- /dev/null +++ b/docs/adr/ADR-005-esquema-declarativo-formularios.md @@ -0,0 +1,102 @@ +# ADR-005 — Esquema declarativo de formularios y estrategia de dos fases + +> Para agregar a `ARCHITECTURE.md` (o `docs/adr/`). Estado: **Aceptada**. +> Fecha: 2026-06-20 · Sprint 4 · Versión objetivo 3.0.0 + +--- + +## Contexto + +El producto es un **motor de formularios/flujos de encuesta**. La aplicación +actual (encuesta "Mapeo de Cuidadores") es su primer caso de uso real, pero la +arquitectura debe servir al producto, no solo a este formulario. + +El rediseño a wizard v3 introduce lógica de flujo significativamente más compleja +que v2: recorridos ramificados (cuidador completo / solo niño / sin vínculo), +mini-wizard de instancias de vínculo familiar (preguntas que se repiten N veces, +una por familiar), y saltos condicionales de sección. + +Existe una restricción de negocio: dos clientes inminentes en 10-20 días requieren +la encuesta funcionando para validación. + +El esquema actual de `questions` ya posee una base declarativa: +- `conditional_rules` JSONB con operadores `eq` / `eq_any` y acción `show` +- Auto-target implícito (la regla afecta a la pregunta que la porta) +- Tipos de pregunta extensibles vía CHECK constraint + +## Problema + +¿Dónde reside la lógica del flujo del formulario? Tres anti-patrones a evitar: +1. **Lógica en el presentador** (frontend con reglas hardcodeadas) — impide + reutilización, genera bugs de sincronización de estado, no escala al producto. +2. **Lógica dispersa en columnas sueltas** de la base — deuda técnica; no soporta + evolución a flujos más complejos sin migrar el esquema entero. +3. **Sobre-ingeniería prematura** — construir un motor genérico universal para + casos hipotéticos antes de conocerlos, retrasando el deadline real. + +## Decisión + +**La lógica del flujo vive en una capa de definición declarativa (datos), +interpretada por un motor en el backend, y renderizada por un frontend sin reglas +de negocio.** + +Se adopta una **estrategia de dos fases**: + +### Fase 1 (Sprint 4 — para el deadline) +- El formulario de cuidadores se define como **esquema declarativo persistido**, + extendiendo el `conditional_rules` JSONB existente y agregando campos + declarativos de instanciación a `questions`. +- Se construye un motor de resolución de flujo **para este formulario** (interpreta + el esquema; calcula qué pantalla sigue desde esquema + estado). +- El frontend wizard es un **renderer** del esquema, sin reglas de negocio. +- NO se construye el intérprete genérico universal todavía. + +### Fase 2 (sprints posteriores — post-deadline, registrada en BACKLOG) +- Se extrae el intérprete a un **motor genérico de formularios**. +- El formulario de cuidadores, ya definido como esquema, se vuelve el primer caso + de prueba del motor — sin reescribir el contrato. + +### Diseño técnico del esquema (Fase 1) +Se extiende `questions` en lugar de crear una tabla de definición separada: +- `conditional_rules` extendido con nuevos operadores (`not_in`, acción `hide`) + para expresar saltos como "Ninguno → salta a sección final". +- Nuevos campos declarativos para instanciación: + - `instance_generator` — marca la pregunta que genera instancias (ej. 4.1) + - `instance_of` — código del generador del que depende una pregunta (ej. 4.2 → "4.1") +- Representación de secciones con nombre y descripción (el "porqué"). + +## Alternativas consideradas + +1. **Lógica en el frontend (rechazada):** rápida para este formulario, pero viola + el principio de producto, no escala, y reproduce los bugs de sincronización ya + vistos en el mockup. +2. **Columnas sueltas tipo `is_instanced` (rechazada):** resuelve hoy, genera deuda; + no soporta flujos más complejos sin migración de esquema. +3. **Motor genérico universal ahora (rechazada):** arquitectónicamente deseable como + fin, pero sobre-ingeniería antes del deadline y con un solo caso de uso conocido + (diseñar en el vacío). Se difiere a Fase 2 cuando haya más casos que informen el + diseño abstracto. +4. **Tabla de definición de flujo separada (diferida):** posible mejor forma para el + motor genérico, pero para un solo formulario es sobre-ingeniería. Se reevalúa en + Fase 2. + +## Consecuencias + +**Positivas:** +- Entrega la encuesta en plazo sin generar retrabajo estructural. +- La lógica en datos habilita guardado parcial, reanudación, validación consistente + y progreso preciso — mejor UX, fuente única de verdad del estado. +- La Fase 2 es generalización del intérprete, no reescritura del contrato. +- El esquema declarativo es versionable y auditable. + +**Negativas / deuda intencional:** +- Fase 1 es más trabajo que parchear el formulario actual. +- El motor de Fase 1 es específico a este formulario (no genérico) — deuda + registrada en BACKLOG, asumida conscientemente, no oculta. +- Extender `conditional_rules` puede requerir reevaluación cuando lleguen flujos + con lógica que el vocabulario actual de operadores no exprese. + +## Cumplimiento (cómo se verifica) +- Revisión de código: el frontend wizard NO contiene reglas de flujo del negocio. +- El flujo es reproducible desde el esquema persistido. +- La deuda de Fase 2 figura en `BACKLOG.md`. \ No newline at end of file