edtech / docs /multi-tenant-architecture.md
CognxSafeTrack
chore: finalize Sprint P2 & P3 optimizations, baseline prisma migrations, and update technical audit docs
cfbb685

Documentation Technique : Architecture B2B SaaS Multi-Tenant

Cette documentation détaille la transition du système d'un produit "Single-Tenant" vers une plateforme B2B SaaS Multi-Tenant capable d'accueillir plusieurs organisations (écoles, entreprises) avec une isolation stricte et une configuration personnalisée.


🏛️ 1. Architecture des Données & Isolation

Modèle de Données (Prisma)

Toutes les entités critiques (Track, Lesson, User, Response, Enrollment) sont désormais liées à une entité Organization.

  • Champ organizationId : Présent et indexé sur chaque table pour garantir des performances de filtrage optimales.
  • Isolation Automatique : Utilisation d'une extension Prisma (packages/database/src/extension.ts) qui injecte automatiquement la clause where: { organizationId } dans toutes les requêtes.

Contexte Tenant

Le contexte est propagé via AsyncLocalStorage (packages/database/src/context.ts) :

  • API : Le tenantMiddleware extrait l'ID depuis les headers (x-organization-id) ou le token auth.
  • Worker : Le contexte est extrait des données du job BullMQ et injecté avant l'exécution du handler.

⚙️ 2. Worker WhatsApp (Engine)

Le worker a été totalement modularisé pour éviter le monolithisme.

Dispatcher Central (index.ts)

Le worker agit comme un dispatcher léger qui délègue les tâches à des JobHandlers spécialisés.

Handlers Spécialisés (src/handlers/)

  • InboundHandler : Point d'entrée unique pour tous les messages texte/audio.
  • AdminHandler : Commandes réservées à l'administration.
  • NudgeHandler : Relances automatiques.
  • EnrollHandler : Gestion des inscriptions aux parcours.
  • MediaHandler : Traitement des images et vidéos.

🧠 3. Moteur d'IA & Pattern Strategy

Le service d'intelligence artificielle (apps/api/src/services/ai/) a été conçu pour la résilience.

Provider Registry

L'IA n'est plus liée à un seul fournisseur. Le ProviderRegistry permet d'enregistrer plusieurs moteurs (Gemini, OpenAI, Mistral) avec leurs capacités :

  • Priority Failover : Si le provider primaire (ex: Gemini) échoue, le système bascule automatiquement sur le secondaire (ex: OpenAI).
  • Capability Routing : Les requêtes sont routées vers le meilleur modèle selon le besoin (Vision pour les images, Whisper pour l'audio, GPT-4o pour le texte complexe).

Personnalisation Dynamique

Chaque organisation peut configurer son propre "Personality Studio". Les prompts sont compilés dynamiquement en fusionnant :

  1. Le template de base du système.
  2. La configuration spécifique de l'organisation (nom du bot, mission, ton).

👁️ 4. Observabilité & Debugging

Logging Multi-Tenant

Le logger (logger.ts) est synchronisé avec le contexte AsyncLocalStorage.

  • Auto-Injection : Chaque ligne de log (info, error) inclut automatiquement l' organizationId s'il est présent dans le contexte d'exécution.
  • Bénéfice : Possibilité de filtrer les logs en temps réel par client dans les outils de monitoring (Cloudwatch, Datadog).

📡 5. Flux de Message (Pipeline)

Le flux de traitement est désormais unifié et asynchrone :

  1. Webhook API : Reçoit le message, identifie l'organisation via le phone_number_id, et ajoute un job handle-inbound dans Redis (BullMQ).
  2. BullMQ : Gère la file d'attente et la persistance des tâches.
  3. Worker : Consomme le job, initialise le contexte tenant, normalise le texte (Wolof/FR), et appelle le handler approprié.
  4. WhatsApp Cloud API : Envoi de la réponse finale via les services utilitaires (whatsapp-cloud.ts).

🧪 6. Stratégie de Test

Le projet utilise Vitest pour garantir la stabilité :

  • Tests Unitaires : Validation de la normalisation du Wolof (normalizeWolof.test.ts).
  • Tests d'Intégration : Simulation des flux métier via des mocks Prisma et BullMQ (OnboardingHandler.test.ts).

🛠️ Maintenance & Évolutions

  • Ajouter un Handler : Créer une classe implémentant JobHandler dans src/handlers/ et l'enregistrer dans index.ts.
  • Ajouter un Provider IA : Créer une classe implémentant LLMProvider et l'ajouter au ProviderRegistry.
  • Migration DB : Toujours utiliser npx prisma migrate dev pour maintenir l'intégrité du schéma multi-tenant.