Pregunta 1 — Terminología base
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
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"
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
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
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
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?
# 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
# 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.
# 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.