Aller au contenu principal

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)

CoucheTechnologie
FrameworkReact 18.3
BundlerVite 6.3
LangageTypeScript strict
StylingTailwind CSS 4.1 + tokens centralisés (tokens.ts)
UI primitivesshadcn/ui + Radix UI
NavigationReact Router 7 (BrowserRouter)
État serveurTanStack Query v5
FormulairesReact Hook Form
AnimationsMotion (ex Framer Motion)
GraphesRecharts
ToastsSonner
IcônesLucide React

Backend (apps/api/)

CoucheTechnologie
FrameworkNestJS 11
LangageTypeScript strict
ORMPrisma 5
Base de donnéesPostgreSQL 16
AuthPassport JWT (access 15 min + refresh 7 j avec rotation)
Validationnestjs-zod (validation runtime des payloads)
TestsJest + supertest + Testcontainers
Documentation APIOpenAPI via @nestjs/swagger
Tâches asynchronesBullMQ + 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ête Authorization: Bearer <token>.
  • Refresh token (long, 7 jours) — stocké en localStorage côté frontend, en table RefreshToken côté backend. À chaque appel POST /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 :

  1. ZodValidationPipe (global) — valide le payload contre le schéma Zod du DTO. Toute erreur retourne 400 Bad Request avec le détail des champs en erreur.
  2. @Roles('ADMIN' | 'TEACHER' | 'LEARNER') + RolesGuard — vérifie le rôle JWT. Toute violation retourne 403 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 database mombiz_dev propres au projet
  • Redis : DB numéro 0 réservée à MomBiz, préfixe mombiz: 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é.