Architecture
Vue d'ensemble
MomBiz Academy est un monorepo TypeScript organisé en npm workspaces, avec un découpage front / back / contrats partagés :
e-learning/
├── apps/
│ ├── frontend-public/ # Zone publique (landing, auth, catalogue) — React + Vite, servie en /
│ ├── frontend-learner/ # Espace apprenante — servi en /learner
│ ├── frontend-teacher/ # Espace formatrice — servi en /teacher
│ ├── frontend-admin/ # Back-office admin — servi en /admin
│ ├── api/ # API REST NestJS + Prisma
│ └── docs/ # Site de documentation Docusaurus (ce site)
├── packages/
│ ├── ui/ # @app/ui — composants, contextes, services API, design tokens partagés
│ └── contracts/ # Schémas Zod partagés entre front et back
├── docs/ # Documentation source (en cours de migration vers apps/docs)
└── package.json # Workspaces racine
Les 4 frontends par rôle partagent @app/ui et sont servis sous une origine unique derrière reverse-proxy (cf. ADR 0054). Routes inchangées (chemins absolus préfixés /learner, /admin…), base Vite par app, redirects inter-app en navigation pleine page.
Stack technique
Frontend (apps/frontend-*/ + packages/ui)
| Couche | Technologie |
|---|---|
| Framework | React 18.3 |
| Bundler | Vite 6.3 |
| Langage | TypeScript strict |
| Styling | Tailwind CSS 4.1 + tokens centralisés (tokens.ts) |
| UI primitives | shadcn/ui + Radix UI |
| Navigation | React Router 7 (BrowserRouter) |
| État serveur | TanStack Query v5 |
| Formulaires | React Hook Form |
| Animations | Motion (ex Framer Motion) |
| Graphes | Recharts |
| Toasts | Sonner |
| Icônes | Lucide React |
Backend (apps/api/)
| Couche | Technologie |
|---|---|
| Framework | NestJS 11 |
| Langage | TypeScript strict |
| ORM | Prisma 5 |
| Base de données | PostgreSQL 16 |
| Auth | Passport JWT (access 15 min + refresh 7 j avec rotation) |
| Validation | nestjs-zod (validation runtime des payloads) |
| Tests | Jest + supertest + Testcontainers |
| Documentation API | OpenAPI via @nestjs/swagger |
| Tâches asynchrones | BullMQ + Redis (emails, certificats — phase ultérieure) |
Contrats partagés (packages/contracts/)
Le package @app/contracts est la source unique de vérité des schémas de données :
- Définit chaque entité métier en Zod (
courseSchema,userSchema, etc.) - Le backend l'utilise pour valider les payloads entrants (
ZodValidationPipe) - Le frontend l'utilise pour typer les formulaires et les réponses API
Avantage : un changement de schéma se propage automatiquement au build TypeScript des deux côtés. Pas de duplication, pas de désynchronisation possible.
Structure des dossiers
Frontend
packages/ui/src/ # @app/ui — partagé par les 4 apps
├── tokens.ts # Tokens de design (couleurs, espacements, typo)
├── types/auth.ts # Types User / UserRole
├── contexts/AuthContext.tsx # Auth state + bootstrap /auth/me
├── components/
│ ├── ui/ # shadcn primitives (non modifiables)
│ └── shared/ # TopHeader, BottomNav, ProtectedRoute, WithAuth, AppRedirect…
├── services/api/ # Clients HTTP par domaine (auth.ts, courses.ts…)
└── styles/tailwind.css # Importe Tailwind + scanne packages/ui + apps/*
apps/frontend-<role>/ # une app par rôle (public, learner, teacher, admin)
├── index.html # base Vite = / · /learner · /teacher · /admin
├── vite.config.ts # alias @app/ui, base, (public : proxy dev)
└── src/
├── main.tsx # QueryClient + AuthProvider + App
└── app/
├── App.tsx # Routes du rôle (chemins absolus inchangés)
└── pages/<role>/ # Pages propres au rôle
Backend
apps/api/src/
├── main.ts # Bootstrap NestJS (port, CORS, prefix /api/v1)
├── app.module.ts # Module racine
├── auth/ # Login, register, JWT, refresh, /auth/me
├── courses/ # CRUD cours, modules, chapitres, sections
├── enrollments/ # Inscriptions apprenantes
├── progress/ # Progression sections + quiz
├── learner-quizzes/ # Vue quiz côté apprenante (sans correctIndex)
├── certificates/ # Génération + partage certificats
├── payments/ # Stub paiement → enrollment
├── admin/ # Back-office : users, courses, stats
├── teacher-dashboard/ # Stats formatrice
└── prisma/ # PrismaService (singleton)
Chaque module backend suit la convention {module}.module.ts + {module}.controller.ts + {module}.service.ts + tests .spec.ts.
Contracts
packages/contracts/src/
├── auth.ts # Login, register, refresh, change password
├── user.ts # User public, update me
├── course.ts # Course, module, chapter, section, content block, quiz
├── admin.ts # Vues admin (users, courses, stats détaillé)
├── teacher.ts # Stats teacher, enrollments
├── payment.ts # Création paiement, statuts
├── progress.ts # Section progress, course progress
├── certificate.ts # Émission, lecture, partage
└── index.ts # Re-exports
Flux de données type — achat d'un cours
┌───────────────┐ GET /courses/public ┌────────────┐
│ Apprenante │ ───────────────────────────▶ │ NestJS API │
│ (Catalogue) │ ◀─── courseListPublic ────── │ │
└───────┬───────┘ └─────┬──────┘
│ clic carte │
▼ ▼
┌───────────────┐ ┌────────────┐
│ Fiche cours │ │ Prisma │
│ /catalog/:id │ ─── POST /payments ────────▶ │ PostgreSQL │
└───────────────┘ └─────┬──────┘
▲ │
│ │ INSERT Payment
│ │ + INSERT Enrollment
│ │ (transaction)
│ ◀── { payment, enrollment } ───────── │
▼ │
┌───────────────┐ │
│ Player cours │ ─── GET /courses/:slug ───────────▶│
│ /course/:slug │ ◀── courseDetailPublic ────────────┤
└───────────────┘ │
│ │
▼ │
POST /progress/sections/:id/complete ─────────────┘
Authentification
L'auth utilise un double token :
- Access token (JWT court, 15 min) — porte la claim
{ sub, email, role }, transmis dans l'en-têteAuthorization: Bearer <token>. - Refresh token (long, 7 jours) — stocké en localStorage côté frontend, en table
RefreshTokencôté backend. À chaque appelPOST /auth/refresh, l'ancien est révoqué et un nouveau est émis (rotation).
Le frontend gère la rotation automatique : sur un 401, apiClient tente un /auth/refresh puis rejoue la requête.
Validation et RBAC
Toutes les requêtes entrantes passent par deux couches :
ZodValidationPipe(global) — valide le payload contre le schéma Zod du DTO. Toute erreur retourne400 Bad Requestavec le détail des champs en erreur.@Roles('ADMIN' | 'TEACHER' | 'LEARNER')+RolesGuard— vérifie le rôle JWT. Toute violation retourne403 Forbidden.
L'isolation par propriété (une formatrice ne voit que ses cours) est implémentée au niveau service, pas au niveau ORM : chaque méthode métier filtre explicitement avec where: { teacherId: user.sub }.
Infrastructure locale
L'environnement de dev utilise une infra Docker mutualisée entre tous les projets de la machine (un seul Postgres, un seul Redis). Le repo ne contient pas de docker-compose.yml — l'infra vit dans ~/dev-infra/ (par convention).
Isolation par projet :
- Postgres : un user
mombiz+ une databasemombiz_devpropres au projet - Redis : DB numéro
0réservée à MomBiz, préfixemombiz:sur toutes les clés
Migrations Prisma
# Générer une migration depuis un changement de schema.prisma (dev local)
npm --workspace apps/api exec prisma migrate dev --name <description>
# Appliquer les migrations existantes (CI / autre machine)
npm --workspace apps/api exec prisma migrate deploy
# Régénérer le client TypeScript après changement
npm --workspace apps/api exec prisma generate
Toute modification destructive (drop de colonne, renommage, changement de type) doit être flaguée explicitement dans la review de la migration. Le repo ne fait pas de SQL manuel : tout passe par Prisma.
Production
L'hébergement n'est pas encore arrêté. Pistes :
- VPS Hetzner ou OVH (2 vCPU / 4 Go pour démarrer)
- Postgres et Redis dédiés à l'application (pas de mutualisation en prod)
- Frontend statique servi par Nginx ou via un CDN (Vercel, Cloudflare Pages)
- API derrière un reverse proxy avec TLS (Caddy ou Nginx)
Les détails seront documentés dans Operations → Déploiement une fois le choix tranché.