6 KiB
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 :
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 :
checkFeatureAccessest verbeux en JSX, le nomcanUserSimulateest backend-coloré (prend unuser, 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.
// 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.tsstrictement identique au backend (ADR 004 respecté à 100%), le code frontend utilisehasAccessetcanSimulate(standards React), signature ergonomique côté frontend. - Inconvénients : une légère indirection (
Ctrl+clicksurhasAccessmè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 :
if (plan === 'premium') { ... }
if (PLANS[plan].exam_mode) { ... }
if (!user.plan === 'free' && simulations_used > 5) { ... }
Obligatoire :
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+ assertionscanUserSimulate/canSimulate: 7+ assertionsgetPlanPermissions: 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.tsstrictement 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
PLANSla rend immédiatement accessible viahasAccess. canSimulatea une signature plus ergonomique côté frontend.
Négatives :
- Indirection légère :
Ctrl+clicksurhasAccessmène àlib.ts, puis un deuxième clic est nécessaire pour arriver àaccess.ts. Mitigation : documenté dansONBOARDING.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 (objetPLANSsource de vérité)- ADR 004 (règle de duplication de
access.ts) TESTS_AUTOMATISES.mdbackend §3, §4, §5 (tests de parité)- Audit backend du 2026-04-17 (confirmation du contenu de
access.ts)