# ADR 005 — Fonctions d'accès : alias frontend-idiomatiques sur l'API backend **Statut :** Accepté **Date :** 2026-04-17 **Décideur :** Hermann **Révision :** 2 (après audit backend du 2026-04-17) --- ## Contexte La vérification des permissions selon le plan utilisateur est la logique la plus critique du frontend. Une erreur ici = une feature Premium accessible gratuitement, ou à l'inverse un utilisateur payant bloqué. La Règle D de `DEVELOPMENT_PRINCIPLES.md` l'énonce clairement : **jamais de `if (plan === 'premium')` dans le code**. Toute vérification passe par un helper centralisé. L'audit backend du 2026-04-17 a confirmé que `src/lib/access.ts` expose trois fonctions : ```typescript export function getPlanPermissions(plan: Plan): Permissions export function canUserSimulate(user: { plan: string; simulations_used: number }): { allowed, reason? } export function checkFeatureAccess(plan: Plan, feature: Feature): boolean ``` ## Contrainte dictée par l'ADR 004 Le fichier `src/entities/user/access.ts` côté frontend **doit être identique au bit près** à `expria-backend/src/lib/access.ts`. Cette règle est non négociable : elle garantit qu'un changement de permission dans le backend est impossible à rater côté frontend. ## Options envisagées ### Option 1 — Noms strictement identiques partout Frontend utilise directement `canUserSimulate()` et `checkFeatureAccess()` dans tout le code. - **Avantages :** un seul nom par fonction dans tout le projet, pas d'indirection, grep universel. - **Inconvénients :** `checkFeatureAccess` est verbeux en JSX, le nom `canUserSimulate` est backend-coloré (prend un `user`, pas un plan), ces noms ne sont pas les standards React (`hasRole`, `hasPermission`, `hasAccess`). ### Option 2 — Alias frontend-idiomatiques dans `entities/user/lib.ts` `access.ts` reste identique au backend. Un fichier séparé `lib.ts` ré-exporte les fonctions sous des noms plus courts et idiomatiques React. ```typescript // src/entities/user/access.ts — IDENTIQUE au backend export function getPlanPermissions(plan) { /* ... */ } export function canUserSimulate(user) { /* ... */ } export function checkFeatureAccess(plan, feature) { /* ... */ } // src/entities/user/lib.ts — alias pour le frontend import { canUserSimulate, checkFeatureAccess, getPlanPermissions, } from './access' /** * Alias frontend-idiomatique de checkFeatureAccess. * Vérifie si un plan a accès à une feature booléenne donnée. */ export const hasAccess = checkFeatureAccess /** * Alias frontend-idiomatique de canUserSimulate. * Signature ergonomique (plan, used) au lieu de ({ plan, simulations_used }). */ export function canSimulate(plan: Plan, simulationsUsed: number) { return canUserSimulate({ plan, simulations_used: simulationsUsed }) } /** * Ré-export direct — même nom qu'au backend. */ export { getPlanPermissions } ``` - **Avantages :** `access.ts` strictement identique au backend (ADR 004 respecté à 100%), le code frontend utilise `hasAccess` et `canSimulate` (standards React), signature ergonomique côté frontend. - **Inconvénients :** une légère indirection (`Ctrl+click` sur `hasAccess` mène à un ré-export). Bénéfice net : la lisibilité des composants React. ## Décision **Option 2** — alias frontend-idiomatiques dans `entities/user/lib.ts`, par-dessus un `access.ts` strictement identique au backend. ### Emplacement du code ``` src/entities/user/ ├── access.ts # COPIE BIT-À-BIT de expria-backend/src/lib/access.ts └── lib.ts # Alias et helpers frontend-spécifiques ``` ### Règle d'utilisation dans le code frontend **Interdit :** ```typescript if (plan === 'premium') { ... } if (PLANS[plan].exam_mode) { ... } if (!user.plan === 'free' && simulations_used > 5) { ... } ``` **Obligatoire :** ```typescript import { hasAccess, canSimulate } from '@/entities/user/lib' if (hasAccess(plan, 'exam_mode')) { ... } const { allowed, reason } = canSimulate(plan, simulationsUsed) if (!allowed) { if (reason === 'quota_reached') openUpgradeModal() } ``` ### Règle de maintenance Toute modification de `access.ts` doit être faite **simultanément** dans les deux dépôts (backend et frontend) dans le même commit logique. Si une fonction est ajoutée ou renommée côté backend, un alias équivalent peut être ajouté dans `lib.ts` **seulement si** le nom backend est inconfortable en contexte React. Sinon, import direct. ### Tests associés (obligatoires) ``` src/entities/user/__tests__/access.test.ts # teste les 3 fonctions backend (parité) src/entities/user/__tests__/lib.test.ts # teste hasAccess et canSimulate (alias) ``` Au minimum : - `checkFeatureAccess` / `hasAccess` : 14+ assertions - `canUserSimulate` / `canSimulate` : 7+ assertions - `getPlanPermissions` : 4 assertions (3 plans + plan invalide) Ces tests calquent leur structure sur `TESTS_AUTOMATISES.md` backend pour garantir la parité des comportements. ## Conséquences **Positives :** - `access.ts` strictement identique au backend : impossible de diverger par accident. - Code frontend lisible et idiomatique : `hasAccess(plan, 'exam_mode')` dans un composant React se lit naturellement. - Un dev React qui arrive trouve les noms qu'il cherche. - Extension automatique : ajouter une permission dans `PLANS` la rend immédiatement accessible via `hasAccess`. - `canSimulate` a une signature plus ergonomique côté frontend. **Négatives :** - Indirection légère : `Ctrl+click` sur `hasAccess` mène à `lib.ts`, puis un deuxième clic est nécessaire pour arriver à `access.ts`. Mitigation : documenté dans `ONBOARDING.md` + commentaire JSDoc sur chaque alias. **À revisiter si :** - Une troisième catégorie de valeurs apparaît dans `PLANS` (ni booléen ni quota). - Un dev senior trouve que les alias ajoutent plus de friction que de valeur. ## Références - `PLANS_TARIFAIRES.md` §3 (objet `PLANS` source de vérité) - ADR 004 (règle de duplication de `access.ts`) - `TESTS_AUTOMATISES.md` backend §3, §4, §5 (tests de parité) - Audit backend du 2026-04-17 (confirmation du contenu de `access.ts`)