Aller au contenu principal

Conventions de code

Langue

ContexteLangue
Code, noms de variables, messages d'erreur techniquesAnglais
Commentaires de codeFrançais
Messages de commitFrançais (cf. Workflows)
Documentation (cette doc et autres .md)Français

Cette double règle vient du fait que le code est lu par des outils anglo-saxons (Stack Overflow, IDEs, IA) tandis que la collaboration humaine se fait en français.

TypeScript

  • Mode strict activé partout (strict: true dans chaque tsconfig.json)
  • Pas de any, pas de @ts-ignore ni @ts-expect-error
  • L'échappatoire au typage est unknown + narrowing explicite, ou un type guard
  • Pas d'inférence cachée : les fonctions exportées ont toujours leur type de retour annoté

Frontend

Styling

  • Tokens design dans packages/ui/src/tokens.ts (importés via @app/ui/tokens) — toujours utiliser ces objets (C, S, T, R, SH, G) plutôt que des valeurs en dur
  • Grille d'espacement 8 px : utiliser les clés S[] (S[2]=8px, S[4]=16px, etc.)
  • Tailwind acceptable pour le layout simple (flex, grid, padding) ; tokens.ts pour les valeurs de design
  • Pas de Tailwind pour font-size, font-weight, line-height — toujours via T.size.*, T.weight.*
  • Inline styles acceptables pour les micro-ajustements ponctuels ; à éviter pour les patterns récurrents (créer un composant)

Organisation

packages/ui/src/ # @app/ui — partagé par les 4 apps
├── components/ui/ # shadcn primitives (à ne pas modifier)
├── components/shared/ # TopHeader, BottomNav, ProtectedRoute…
├── services/api/{domain}.ts # Client HTTP par domaine
├── contexts/ # Providers React (auth, etc.)
└── tokens.ts # Tokens de design

apps/frontend-<role>/src/app/
├── App.tsx # Routes du rôle
└── pages/<role>/{Domain}Page.tsx # Une page = un composant racine
  • Pages par rôle : apps/frontend-learner/src/app/pages/learner/LearnerDashboardPage.tsx
  • Suffixe Page.tsx pour les composants racine de route
  • Alias : @/src/ de l'app courante · @app/ui/*packages/ui/src/*
  • Primitives de layout : privilégier <Stack>, <Row>, <Grid> (cf. design system)

Données serveur

  • TanStack Query v5 pour tout fetch authentifié — jamais de useEffect + fetch brut
  • Une queryKey par requête, structurée en tableau : ['admin', 'users', { page, role }]
  • Mutations : utiliser useMutation + invalidation manuelle de la queryKey concernée
  • Erreurs API : intercepter ApiError (notre wrapper), afficher un toast sonner

Backwards-compat

Pas de feature flags ni de shims pour de la rétrocompatibilité dans le frontend MVP. Quand une page est refactorée, on supprime l'ancienne, on supprime les imports orphelins, on n'écrit pas de "legacy".

Backend

Modules

Chaque domaine vit dans un module Nest dédié :

apps/api/src/{domain}/
├── {domain}.module.ts # Déclaration des controllers + services
├── {domain}.controller.ts # Endpoints HTTP, @Roles, @UseGuards
├── {domain}.service.ts # Logique métier, appels Prisma
├── dto/ # DTOs Zod importés depuis @app/contracts
└── {domain}.service.spec.ts # Tests Jest

Configuration

  • @nestjs/config charge .env au boot
  • Le fichier config/env.schema.ts définit le schéma Zod des variables d'environnement attendues — l'app refuse de démarrer si une variable manque ou est invalide
  • Préfixe global : app.setGlobalPrefix('api/v1') dans main.ts

RBAC

  • Decorator @Roles('ADMIN') sur les controllers ou les méthodes — le RolesGuard vérifie la claim JWT
  • Isolation par propriété : implémentée dans le service (where: { teacherId: user.sub, ... }), jamais en s'appuyant uniquement sur le rôle
  • Ne jamais bypasser un guard — si un endpoint doit être public, retire-le des modules protégés ou utilise @Public() explicitement

Erreurs HTTP

  • 400 — payload invalide (validation Zod)
  • 401 — token absent ou invalide
  • 403 — rôle ou propriété insuffisants
  • 404 — ressource introuvable (ou inaccessible — n'expose pas la différence)
  • 409 — conflit (ex : email déjà pris)
  • 500 — bug serveur (à logger en priorité)

Toujours retourner via les exceptions Nest (ForbiddenException, NotFoundException, etc.), jamais de res.status(...).

Base de données

Migrations

  • Prisma uniquement, pas de SQL manuel
  • prisma migrate dev --name <description> en local, prisma migrate deploy en prod et CI
  • Revue obligatoire des migrations avant merge — toute opération destructive (drop de colonne, renommage, changement de type) doit être flaguée
  • Les migrations vivent dans apps/api/prisma/migrations/ et sont commitées au repo

Soft delete

  • Toutes les entités métier ont un champ deletedAt: DateTime?
  • "Supprimer" en UI = UPDATE ... SET deletedAt = NOW(), jamais DELETE
  • Les requêtes filtrent par défaut WHERE deletedAt IS NULL — explicite via la clause where

Sécurité SQL

  • Pas de $queryRaw non paramétré — toujours en template tagué :

    prisma.$queryRaw`SELECT * FROM users WHERE id = ${userId}`
  • $executeRaw strictement paramétré avec interpolation Prisma

API

Validation

  • Validation input via Zod — schémas définis dans packages/contracts, pipeline ZodValidationPipe global
  • Source unique de vérité : le frontend importe les mêmes types pour ses formulaires
  • Pas de validation manuelle dans les controllers — si le payload arrive jusqu'au service, il est valide

Pagination

Format unique pour toutes les listes :

{
"items": [...],
"total": 123,
"page": 1,
"pageSize": 20
}

Paramètres query : ?page=1&pageSize=20. Limite max pageSize à 100 côté backend.

Sécurité

  • Zéro secret en git — tout dans .env (ignoré), .env.example commité avec valeurs factices
  • Hash mot de passe : Argon2id via le package argon2 (jamais bcrypt, jamais SHA brut)
  • JWT : access 15 min + refresh 7 jours avec rotation et révocation côté DB
  • Helmet + @nestjs/throttler activés au boot
  • CORS : whitelist stricte (http://localhost:5173 en dev, domaine prod en prod)
  • Validation API-side systématique — ne jamais faire confiance au client

Tests

  • Backend : Jest + supertest + Testcontainers (Postgres réel)
    • Lancer : npm run test --workspace apps/api
    • Tests .spec.ts à côté du fichier testé
  • Frontend : pas de test automatisé en MVP (priorité produit)
  • E2E : à mettre en place après stabilisation MVP (Playwright envisagé)

Code propre

  • Pas de commentaire qui explique le quoi — le code bien nommé suffit
  • Commentaires utiles : le pourquoi non évident, les invariants cachés, les contournements de bug
  • Pas de docstring multi-paragraphes sur des fonctions évidentes — une ligne max
  • Pas de fichier de planning ou d'analyse intermédiaire dans le repo — ces réflexions vivent dans les issues GitLab ou les ADR

Anti-patterns à éviter

  • Une fonction qui prend plus de 5 paramètres positionnels — passer un objet
  • Une variable *Manager, *Helper, *Util sans plus de précision — chercher le vrai concept métier
  • Une fonction qui mélange validation, transformation et persistance — séparer
  • Un composant React de plus de 300 lignes — découper en sous-composants ou en hooks
  • Un useEffect avec dépendances complexes pour synchroniser deux états — repenser le state shape