ADR-001 — Architecture par module de feature
Statut
Accepté
Date
2026-07-08 (documenté rétroactivement ; le pattern précède cet enregistrement)
Contexte
L'application a grandi historiquement autour de src/screens/ (destinations de navigation) et src/components/ (UI partagée), avec de la logique transverse éparpillée dans src/utils/. À mesure que les écrans accumulaient des arbres privés profonds, les frontières se sont brouillées : logique métier, accès aux données et UI se mélangeaient librement, sans encapsulation imposée. Toute nouvelle fonctionnalité complexe n'avait d'autre foyer qu'« un énième sous-arbre d'écran ».
Décision
Introduire des modules de feature sous src/features/. Une feature est une tranche verticale autonome qui expose une API publique restreinte via son barrel index.ts ; les consommateurs n'importent que depuis ce barrel.
La structure interne canonique (pleinement réalisée par unitPreference) :
featureName/
├── index.ts API publique (le seul point d'entrée)
├── domain/ logique métier pure, sans dépendance framework
├── adapter/ mapping front ↔ backend
├── api/ | graphql/ opérations GraphQL + types
├── hooks/ état + orchestration (store zustand / context)
├── components/ UI présentationnelle
├── screens/ destinations de navigation (le cas échéant)
├── context/ React context à portée de la feature (le cas échéant)
└── test-utils/ mocks réutilisables pour cette feature
Les dépendances circulaires à l'intérieur de src/features/ sont interdites en erreur dure via dependency-cruiser (no-circular-features, appliqué en CI). Deux implémentations de référence existent : changeMode (centré context/hook) et unitPreference (entièrement en couches — le modèle préféré).
Conséquences
Positives
- Surface publique claire ; les internes peuvent être refactorés librement.
- Un
domain/pur testable sans React. - Dépendances acycliques imposées à l'intérieur des features.
- Un blueprint reproductible pour les nouveaux travaux.
Négatives / Compromis
- Deux paradigmes coexistent pendant la migration (legacy
screens/+components/vsfeatures/), ce qui prête à confusion tant que le code legacy n'est pas migré. - Plus de dossiers/cérémonie en amont pour les petites features.
- La frontière legacy→feature prévue est déclarée dans
.dependency-cruiser.js(LEGACY_PATH_REGEX) mais pas encore appliquée.
Alternatives considérées
- Tout garder dans
screens/+components/— rejeté : aucune encapsulation, couplage croissant. - Uniquement des « feature slices » Redux-Toolkit — rejeté : cela adresse l'état, pas la tranche verticale complète (domaine/api/UI) ni les frontières d'import.