5.0 KiB
ADR-005 — Esquema declarativo de formularios y estrategia de dos fases
Para agregar a
ARCHITECTURE.md(odocs/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_rulesJSONB con operadoreseq/eq_anyy acciónshow- 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:
- Lógica en el presentador (frontend con reglas hardcodeadas) — impide reutilización, genera bugs de sincronización de estado, no escala al producto.
- 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.
- 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_rulesJSONB existente y agregando campos declarativos de instanciación aquestions. - 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_rulesextendido con nuevos operadores (not_in, acciónhide) 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
- 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.
- Columnas sueltas tipo
is_instanced(rechazada): resuelve hoy, genera deuda; no soporta flujos más complejos sin migración de esquema. - 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.
- 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_rulespuede 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.