Saltar al contenido principal

Sistema de Registro del producto

El Sistema de Registro del producto (SoR) es la capa de datos de cara al cliente — datos del tenant, modelados en @tedos/domain (Drizzle/Postgres) en un esquema Postgres product dedicado. Es separado del grafo del Plan Engine del flujo de trabajo: los dos nunca comparten tablas (ver la separación de las dos bases de datos en Mapa de la plataforma).

Se entregó en el esfuerzo E1 (la "MVP slice 1 de Comprender"), centrado en identidad: una persona única y unificada deduplicada entre los canales de un cliente, con datos fiscales, sobre infra real con tenancy + roles del lado del servidor. Comprender es el tenant #1 — ver Comprender.

Qué entregó E1

  • Un paquete SoR del producto — @tedos/domain — en un esquema Postgres product dedicado.
  • Un contrato compartido congelado en @tedos/shared (zod-como-fuente + tipos TypeScript inferidos) para project / person / person_identity más las formas de los endpoints. Tanto la API como el admin lo importan — consistencia por construcción.
  • Auth + tenancy en apps/api (Clerk): una guardia requireProject mapea una org de Clerk → projectId + rol; el operador es superadmin. Cada ruta /p/:id/* se autoriza del lado del servidor.
  • Un shell de admin de dos niveles (plataforma + contexto de proyecto) — ver admin.
  • Dedupe por clave de coincidencia (email / teléfono / RFC): la misma persona desde dos canales se unifica en un registro, con una auditoría de fusión append-only.

Modelo de datos (@tedos/domain, esquema product)

TablaPropósito
projectEl tenant (= una org de Clerk = un cliente) — clerk_org_id, name, status, domain
personUna persona unificada dentro de un proyecto — full_name, email, phone, rfc (PII fiscal, enmascarada en presentación)
person_identityUn registro de fuente/canal para una persona — channel, external_ref, raw (jsonb)
person_mergeLog de auditoría de fusión append-only

Claves de coincidencia — casi-únicas en (project_id, email), (project_id, phone), (project_id, rfc); el servicio de fusión/dedupe enlaza con ellas. El alcance por tenant (project_id) es obligatorio en cada fila de producto.

Superficie API (apps/api, Fastify v5)

Todas las rutas detrás de la guardia de auth/tenancy (org de Clerk + rol; superadmin = cualquier proyecto). El alcance por tenant es del lado del servidor, nunca confiado del cliente.

MétodoRutaQué
GET / POST/projectsLista / crea los proyectos del operador
GET/projects/:idUn proyecto
GET/p/:id/studentsLista + busca las personas del proyecto
GET/p/:id/students/:sidUna persona (vista única)
POST / PATCH/p/:id/studentsCrea / actualiza una persona
POST/p/:id/students/mergeFusiona dos personas (dedupe)
GET/p/:id/summaryResumen del proyecto

El admin alcanza estas a través de un proxy del lado del servidor, de modo que la sesión de Clerk + el contexto de tenant se inyectan en el servidor — ver admin.

Roles

Una membresía de org de Clerk mapea a un rol de proyecto: Owner · Sales · Finanzas · Soporte · Alumno. El operador es superadmin (cualquier proyecto). El rol restringe la navegación de Nivel 2 (admin) y la autorización en cada ruta /p/:id/* (API).

Aislamiento: esquema-por-cliente

E1 entregó un único esquema product. El siguiente paso de arquitectura (ADR-007) mueve a aislamiento esquema-por-cliente — un clúster Postgres, un esquema por proyecto, un esquema de control/registro para la lista de proyectos, y un accesor de BD con alcance por tenant que selecciona el esquema. Esto hace trivial una exportación limpia por cliente ("los clientes son dueños de sus datos y pueden exportarlos"). Ver Datos, memoria y asistente.

La regla dura

Según ADR-006, los datos de tenant/cliente nunca entran al grafo del flujo de trabajo. El SoR del producto (esquema product) y el grafo del Plan Engine (esquema plan) nunca comparten tablas. Tal como está construido, ambos esquemas viven hoy en el mismo clúster Fly Managed Postgres — un compromiso documentado (aún no hay datos reales de tenant) con una ruta de actualización registrada hacia un clúster dedicado antes de volumen real de PII de tenant. Ver Decisiones (ADR-006/007).