Le contexte
CoachPro est une plateforme de suivi sportif et nutritionnel, pensée API-First et SaaS dès le premier jour. Phase 1 : un usage personnel (tracking poids, performances, diète). Phase 2 : une plateforme pour coachs, où chaque coach gère ses clients, leur assigne des programmes et suit leur progression.
L'application est volontairement découpée en un backend API unique (Symfony / API Platform) consommé par un client mobile React Native / Expo. Aucune logique métier ne vit côté client — le même backend servira demain le web et d'autres coachs.
La sécurité multi-tenant : le vrai défi
Dès qu'on parle de plusieurs coachs et de leurs clients, une question devient critique : comment garantir qu'un coach ne voie jamais les données d'un autre ? J'ai répondu par une défense en profondeur à deux couches complémentaires :
- Lectures — une extension Doctrine (
CurrentUserExtension) injecte automatiquement leWHEREde scoping sur toute collection ou item exposé par API Platform. Ajouter une nouvelle entité « possédée » = l'inscrire dans une map, et elle est protégée. Aucun filtre oublié possible. - Écritures & accès direct — un Voter unique (
OwnedResourceVoter) gère les autorisations (VIEW / EDIT / CREATE) pour toutes les entités possédées : soi-même → ok ; client de mon périmètre coach → ok ; sinon refus.
En complément : JWT pour l'authentification stateless, UUID v7 non énumérables (pas de /users/1, /users/2), et un contrôle securityPostDenormalize sur les POST pour empêcher la création de ressources au nom d'un autre utilisateur.
L'IA, mais cadrée
Le coach IA s'appuie sur Mistral AI pour donner des conseils personnalisés et expliquer les recalibrages d'objectifs. Le principe que j'ai posé : l'IA explique, elle ne décide pas. Les calculs (régression sur les pesées, projection d'objectif) sont déterministes ; le LLM ne fait que reformuler la décision en langage clair. Un garde-fou de périmètre limite le chatbot au sport et à la nutrition, et refuse les sujets hors cadre.
Les traitements lourds (recalibrage d'objectif après une pesée) tournent en asynchrone via Symfony Messenger, hors du cycle requête.
La stack
Backend : PHP 8.4, Symfony, API Platform 4.3, Doctrine ORM, PostgreSQL, LexikJWT, Symfony Messenger, Symfony UID (UUID v7), Mistral AI. Mobile : React Native 0.81 + Expo (build cloud EAS). Front web : Tailwind 4 + DaisyUI via AssetMapper.
Ce que ce projet démontre
La capacité à concevoir une architecture SaaS multi-tenant sécurisée by design, à appliquer le principe de défense en profondeur, et à intégrer l'IA de façon responsable — avec une séparation nette entre calcul déterministe et reformulation par le LLM.