CULTIVA IA

Sesión de Interrogatorio de Dominio — NutriFlow SaaS

Módulo Suscripciones v0.3
16 Jun 2026 · 6 preguntas · 4 términos resueltos
Sesión activa — NutriFlow / Módulo de Suscripciones
Pregunta 6 de ~8
6
Preguntas
4
Términos resueltos
2
Contradicciones
2
ADRs generados
Pregunta 1 — Terminología base
1 Claude (interrogador)
En el plan usas "account", "cuenta", "clínica" y "organización" de forma intercambiable para referirte a quien paga. Sin embargo, en src/orgs/Organization.ts el modelo ya se llama Organization. ¿Confirmamos Organization como término canónico, o prefieres Clinic dado que todos tus clientes actuales son clínicas dietéticas?
Respuesta recomendada: Organization — es el término genérico y el código ya lo usa; futuras expansiones a gimnasios o hospitales seguirán encajando sin renombrar.
Álvaro (tech lead)
Confirmamos Organization. Aunque ahora son clínicas, no queremos limitar el modelo. Ya el código usa Organization, tiene sentido.
Término resuelto: Organization = "entidad que contrata y paga NutriFlow". Evitar: Clinic, Account, Cuenta. → CONTEXT.md actualizado.
Pregunta 2 — Roles de usuario
2 Claude (interrogador)
El plan habla de "usuarios", "dietistas" y "clientes finales" — a veces con "cliente" significando el dietista y otras veces el paciente. En el código, src/patients/Patient.ts y src/orgs/User.ts son modelos distintos. ¿Confirmamos que User es el profesional dietista que trabaja dentro de una Organization, y Patient es la persona atendida que nunca accede directamente al sistema?
Respuesta recomendada: Sí, esa separación ya existe en el código. Hay que eliminar "cliente" como término — es radicalmente ambiguo en este dominio.
Álvaro (tech lead)
Exacto. User = dietista con login. Patient = persona atendida, sin acceso propio. "Cliente" queda vetado del vocabulario del equipo.
Términos resueltos: User (profesional con acceso) y Patient (persona atendida, sin acceso). Evitar: cliente, usuario final. → CONTEXT.md actualizado.
Pregunta 3 — Contradicción detectada: "cancel"
3 Claude (interrogador)
El plan dice: "cuando una Organization cancela su cuenta, queremos archivar todo". Pero en src/orgs/Organization.ts el campo es status: 'active' | 'suspended' | 'deleted' — no hay estado archived. Y en src/billing/Subscription.ts existe cancelledAt que sólo marca la fecha de baja, no archiva datos. ¿Cuál es el comportamiento exacto cuando se cancela? ¿"Archivar" implica un estado nuevo, soft-delete, o simplemente el estado suspended + cancelledAt relleno?
Respuesta recomendada: Usar suspended para cancelación reversible (impago, baja temporal) y deleted como soft-delete definitivo. "Archived" es un concepto que no necesitamos — añadir estado nuevo añade complejidad innecesaria.
Álvaro (tech lead)
Tienes razón, "archivar" lo usaba informalmente. Adoptamos: Cancellation = pasar a suspended con cancelledAt. Los datos no se borran hasta 90 días después (deleted). "Archived" queda eliminado del vocabulario.
Contradicción resuelta: plan decía "archivar" pero el código tiene suspended/deleted. Se adopta el modelo del código. Pendiente: actualizar plan de arquitectura.
Término resuelto: Cancellation = transición a suspended. El periodo de retención de 90 días antes de deleted se documenta como regla de negocio. → CONTEXT.md actualizado.
Pregunta 4 — Plan / Diet / NutritionPlan
4 Claude (interrogador)
El plan menciona "dieta", "plan" y "programa" como sinónimos. En el dominio nutricional, ¿un Plan es la prescripción semanal de comidas para un paciente concreto, o es una plantilla reutilizable? Necesito saber si un User crea plantillas y luego las asigna a Patients, o si cada vez crea el plan desde cero para cada Patient.
Respuesta recomendada: Separar en dos términos: PlanTemplate (plantilla reutilizable creada por el dietista) y NutritionPlan (la asignación concreta a un Patient). Esto evita confusión en el código y el UI.
Álvaro (tech lead)
¡Eso es exactamente la distinción que necesitábamos! Adoptamos PlanTemplate y NutritionPlan. "Dieta" y "programa" quedan vetados — son palabras del usuario final, no del dominio técnico.
Términos resueltos: PlanTemplate (plantilla) y NutritionPlan (asignación concreta a Patient). Evitar: diet, dieta, programa, plan (ambiguo). → CONTEXT.md actualizado.
Pregunta 5 — Decisión arquitectónica: proveedor de pagos
5 Claude (interrogador)
Tienes pendiente elegir Stripe vs. Lemon Squeezy para los cobros de Subscription. En src/billing/Subscription.ts ya hay planId que apunta a un proveedor externo. Stripe cubre Europa con SCA nativa y ofrece APIs de MRR; Lemon Squeezy actúa como Merchant of Record (elimina la gestión de IVA UE). ¿NutriFlow se registrará como empresa en España para gestionar el IVA propio, o prefiere externalizar eso?
Respuesta recomendada: Lemon Squeezy si el equipo es pequeño y quiere evitar gestión fiscal multi-país. Stripe si se espera escala y se quiere control total sobre el checkout y reporting.
Álvaro (tech lead)
Vamos con Stripe. Tenemos gestor fiscal en España, queremos control del checkout y ya en 6 meses necesitaremos reporting de MRR detallado. Lemon Squeezy quedaría pequeño.
ADR generado: decisión hard-to-reverse, sorprendente sin contexto y con alternativa real. → ADR-0001-stripe-as-billing-provider.md creado.
Pregunta 6 — Contradicción: datos de Patient y multi-tenancy
6 Claude (interrogador)
El plan dice "los datos de pacientes van en el tenant de la clínica". Pero src/patients/Patient.ts no tiene campo organizationId — está en un contexto separado sin referencia directa. Si una Organization se cancela (pasa a suspended), ¿qué ocurre con los Patients? ¿Siguen siendo accesibles al dietista para exportar durante los 90 días, o se bloquean al instante?
Respuesta recomendada: Los Patients deben pertenecer a una Organization (añadir organizationId a Patient). Durante suspended, el acceso es read-only para permitir exportación. En deleted se anonimiza según RGPD.
Álvaro (tech lead)
Totalmente de acuerdo. El modelo de datos tiene un bug latente — Patient debería tener organizationId. Y el acceso read-only en suspended es un requisito de RGPD. Creamos tarea técnica urgente.
Contradicción crítica: Patient no tiene FK a Organization. Bug de diseño detectado antes de producción. → Tarea técnica P0 creada.
ADR generado: decisión de aislamiento de datos de Patient por Organization con implicaciones RGPD. → ADR-0002-patient-data-tenancy.md creado.
Próximas preguntas
Pendientes en cola:
  • ¿Qué es un Subscription — el contrato con la Organization o el acceso de cada User individual?
  • ¿Un NutritionPlan puede tener múltiples versiones, o sólo la última es válida?
CONTEXT.md
4 términos
# NutriFlow — Billing & Subscriptions Contexto de negocio para el módulo de suscripciones. Gestiona contratos, acceso y ciclo de vida de Organizations. ## Language **Organization**: Entidad que contrata y paga NutriFlow. _Evitar_: Clinic, Account, Cuenta, Client **User**: Profesional (dietista) con credenciales de acceso, que pertenece a exactamente una Organization. _Evitar_: cliente, usuario final **Patient**: Persona atendida por un User. No tiene acceso al sistema. Pertenece a una Organization via organizationId. _Evitar_: cliente, usuario final, paciente (en código) **PlanTemplate**: Plantilla de alimentación reutilizable creada por un User. No está asociada a ningún Patient. _Evitar_: dieta, programa, plan (sin calificador) **NutritionPlan**: Asignación concreta de un PlanTemplate (o plan ad-hoc) a un Patient específico, con fechas de vigencia. _Evitar_: dieta, programa **Cancellation**: Transición de una Organization a estado suspended con cancelledAt registrado. Los datos son accesibles en modo read-only por 90 días; luego pasan a deleted (soft-delete con anonimización RGPD). _Evitar_: archivar, dar de baja, desactivar ## Relationships - Una **Organization** tiene N **Users** - Un **User** crea N **PlanTemplates** - Un **User** asigna N **NutritionPlans** a N **Patients** - Un **Patient** pertenece a exactamente una **Organization** - Una **Cancellation** congela la Organization en 90d ## Flagged ambiguities - "cliente" usado para dietista Y paciente → resuelto: User (dietista) vs Patient (persona atendida) - "plan/dieta/programa" → resuelto: PlanTemplate + NutritionPlan - "archivar" vs código → resuelto: suspended + 90d + deleted - Patient sin organizationId → BUG detectado, tarea P0
ADR-0001-stripe-billing.md
ADR
# Stripe como proveedor de pagos Elegimos Stripe (no Lemon Squeezy) para gestionar las Subscriptions de Organizations porque necesitamos control total sobre el checkout, reporting de MRR y SCA europeo, y contamos con gestor fiscal en España que asume el IVA. **Considered options:** - Lemon Squeezy (Merchant of Record) — elimina gestión fiscal pero limita el control de UI y analytics. - Stripe — control total, más setup pero escala mejor. **Consequences:** - Necesitamos integración con AEAT para IVA español. - El campo Subscription.planId referenciará Stripe Price IDs. - Webhook de Stripe actualiza el estado de Organization.
ADR-0002-patient-tenancy.md
ADR
# Patient data pertenece al tenant Organization Cada Patient está vinculado a exactamente una Organization via organizationId (FK). Al hacer Cancellation, los datos son read-only 90 días; al pasar a deleted se anonimizan conforme a RGPD Art.17. **Consequences:** - Añadir organizationId a Patient (tarea P0 detectada en sesión de interrogatorio, 2026-06-16). - El contexto patients/ no puede operar sin el contexto orgs/ — dependencia explícita documentada.