ADR-003 — Gestion d'état à trois niveaux (Redux + Zustand + Context)
Statut
Accepté
Date
2026-07-08 (documenté rétroactivement)
Contexte
L'application a de l'état à des portées très différentes : données de session persistées globalement (token d'auth, utilisateur, files offline, langue, thème), état UI/domaine local à une feature, et état partagé à portée réduite (l'appareil BLE connecté, les dialogs). Faire passer tout cela par un unique store global crée du boilerplate et du couplage ; l'éparpiller au cas par cas crée de l'incohérence.
Décision
Utiliser trois niveaux, chacun pour une portée distincte :
- Redux (global, persisté) —
src/store/. Redux Toolkit configure le store ; les reducers sont des ducks écrits à la main (constante d'action + creator +switch+ selectors par fichier), persistés viaredux-persist. Pour l'état à l'échelle de l'app, inter-écrans ou persisté :token,user,online,trackedQueries,syncManager,measures,language,themeMode, … - Zustand (local à une feature) — un store léger à l'intérieur d'une feature (ex.
unitPreference/hooks/unitPreference.store.ts) pour l'état possédé par une seule feature. - React Context (portée réduite) — pour une préoccupation partagée dans un sous-arbre : appareil BLE et dialogs (
src/utils/context/), et contextes de feature (changeMode/context/).
Ligne directrice : persisté/global → duck Redux ; possédé par une feature → zustand ; partagé dans un sous-arbre/éphémère → context. N'ajoutez pas de duck global pour de l'état local à une feature.
Conséquences
Positives
- Chaque portée utilise l'outil le plus léger qui convient ; moins de boilerplate pour l'état local à une feature.
- Les nouvelles features peuvent gérer leur propre état sans toucher au store global.
- La whitelist persistée reste petite et intentionnelle.
Négatives / Compromis
- Trois bibliothèques d'état à apprendre.
- La frontière est une ligne directrice, non imposée — risque de dérive (quelque chose de local à une feature qui fuit dans Redux).
- Les ducks sont le style legacy (
switchmanuel), pascreateSlice, alors que RTK est disponible — incohérent avec les conventions RTK modernes.
Alternatives considérées
- Redux partout — rejeté : trop de boilerplate pour l'état local à une feature, et couple les features au store global.
- Migrer tous les ducks vers
createSlicede RTK — reporté : gros changement mécanique à faible bénéfice immédiat ; les nouvelles features utilisent simplement zustand. - Uniquement Zustand/Context (abandonner Redux) — rejeté : redux-persist + les ducks de file offline sont porteurs et éprouvés.