# Analyse des Incohérences entre ADR et Règles Métier **Date d'analyse** : 2026-01-28 **Analysé par** : Audit Architecture RoadWave **Scope** : 18 ADR × Règles Métier (17 fichiers) --- ## Résumé Exécutif Cette analyse a identifié **15 incohérences** entre les décisions d'architecture (ADR) et les règles métier du projet RoadWave. ### Répartition par Sévérité | Sévérité | Nombre | % Total | Statut | Action Required | |----------|--------|---------|--------|-----------------| | 🔴 **CRITICAL** | 2 | 13% | ✅ **RÉSOLU** | ~~avant implémentation~~ | | 🟠 **HIGH** | 4 | 27% | ⏳ 3 restants (1 résolu) | Résolution Sprint 1-2 | | 🟡 **MODERATE** | 8 | 53% | ⏳ En cours | Résolution Sprint 3-5 | | 🟢 **LOW** | 1 | 7% | ⏳ En cours | À clarifier lors du développement | ### Impact par Domaine | Domaine | Nombre d'incohérences | Criticité maximale | |---------|----------------------|-------------------| | Streaming & Géolocalisation | 3 | 🔴 CRITICAL | | Données & Infrastructure | 3 | 🟠 HIGH | | Authentification & Sécurité | 2 | 🟠 HIGH | | Tests & Qualité | 2 | 🟡 MODERATE | | Coûts & Déploiement | 3 | 🟡 MODERATE | | UX & Engagement | 2 | 🟡 MODERATE | --- ## 🔴 Incohérences Critiques (Blocantes) ### #1 : HLS ne supporte pas les Notifications Push en Arrière-plan **Statut** : ✅ **RÉSOLU** (ADR-019 créé) | Élément | Détail | |---------|--------| | **ADR concerné** | ADR-002 (Protocole Streaming) | | **Règle métier** | Règle 05, section 5.1.2 (Mode Piéton, lignes 86-120) | | **Conflit** | HLS est unidirectionnel (serveur→client), ne peut pas envoyer de push quand l'app est fermée | | **Impact** | Mode piéton non fonctionnel : notifications "Point d'intérêt à 200m" impossibles | **Scénario d'échec** : ``` Utilisateur: Marie se promène, app fermée Position: 150m de la Tour Eiffel Attendu: Push notification "🗼 À proximité: Histoire de la Tour Eiffel" Réel: Rien (HLS ne peut pas notifier) ``` **Solution implémentée** : - ✅ **ADR-019** : Architecture hybride WebSocket + Firebase Cloud Messaging - Phase 1 (MVP) : Push serveur via FCM/APNS - Phase 2 : Geofencing natif iOS/Android pour mode offline **Actions requises** : - [ ] Backend : Implémenter endpoint WebSocket `/ws/location` - [ ] Backend : Worker PostGIS avec requête `ST_DWithin` (30s interval) - [ ] Mobile : Intégrer Firebase SDK (`firebase_messaging`) - [ ] Tests : Validation en conditions réelles (10 testeurs, Paris) --- ### #2 : Latence HLS Incompatible avec ETA de 7 Secondes **Statut** : ✅ **RÉSOLU** (ADR-002 mis à jour) | Élément | Détail | |---------|--------| | **ADR concerné** | ADR-002 (Protocole Streaming, lignes 40-41) | | **Règle métier** | Règle 05 (lignes 16-20), Règle 17 (lignes 25-30, 120-124) | | **Conflit** | ETA de 7s avant le point, mais HLS a 5-30s de latence → audio démarre APRÈS avoir dépassé le point | | **Impact** | UX catastrophique : utilisateur entend "Vous êtes devant le château" 100m APRÈS l'avoir dépassé | **Calcul du problème** (90 km/h = 25 m/s) : ``` t=0s → Notification "Suivant: Château dans 7s" (175m avant) t=7s → Utilisateur arrive au château t=15s → HLS démarre (latence 15s) Résultat: Audio démarre 200m APRÈS le point ❌ ``` **Solution implémentée** : - ✅ **ADR-002 mis à jour** : Section "Gestion de la Latence et Synchronisation Géolocalisée" - Pre-buffering à ETA=30s (15 premières secondes en cache local) - ETA adaptatif : 5s si cache prêt, 15s sinon - Mesure dynamique de latence HLS par utilisateur **Actions requises** : - [ ] Backend : Endpoint `/api/v1/audio/poi/:id/intro` (retourne 15s d'audio) - [ ] Mobile : Service `PreBufferService` avec cache local (max 100 MB) - [ ] Mobile : Loader visuel avec progression si buffer > 3s - [ ] Tests : Validation synchronisation ±10m du POI --- ## 🟠 Incohérences Importantes (Sprint 1-2) ### #3 : Souveraineté des Données (Français vs Suisse) **Statut** : ✅ **RÉSOLU** (ADR-008 mis à jour) | Élément | Détail | |---------|--------| | **ADR concernés** | ADR-004 (CDN, ligne 26), ADR-008 (Auth, mis à jour) | | **Règle métier** | Règle 02 (RGPD, section 13.10) | | **Conflit** | ADR-004 revendique "100% souveraineté française" mais ADR-008 utilisait Zitadel (entreprise suisse) | | **Impact** | Contradiction marketing + risque juridique si promesse "100% français" | **Solution implémentée** : **Self-hosting Zitadel sur OVH France** - ✅ Container Docker sur le même VPS OVH (Gravelines, France) - ✅ Base de données PostgreSQL partagée (schéma séparé pour Zitadel) - ✅ Aucune donnée ne transite par des serveurs tiers - ✅ Souveraineté totale garantie : 100% des données en France - ✅ Cohérence complète avec ADR-004 (CDN 100% français) **Changements apportés** : - ✅ ADR-008 mis à jour avec architecture self-hosted détaillée - ✅ TECHNICAL.md mis à jour (tableau + diagramme architecture) - ✅ Clarification : Zitadel est open source, donc aucune dépendance à une entreprise suisse **Actions complétées** : - [x] Décision validée : Self-host sur OVH - [x] ADR-008 mis à jour avec architecture self-hosted - [x] TECHNICAL.md mis à jour --- ### #4 : ORM sqlc vs Types PostGIS | Élément | Détail | |---------|--------| | **ADR concernés** | ADR-013 (ORM, lignes 12, 33-40), ADR-005 (BDD, lignes 47-56) | | **Règle métier** | N/A (problème technique pur) | | **Conflit** | sqlc génère types Go depuis SQL, mais PostGIS geography/geometry ne mappent pas proprement | | **Impact** | Risque de type `interface{}` ou `[]byte` pour géographie → perte de type safety revendiquée | **Nature du problème** : sqlc génère du code Go depuis SQL, mais les types PostGIS (`geography`, `geometry`) ne sont pas mappés proprement en Go. Résultat : types opaques (`[]byte`, `interface{}`) qui perdent la **type safety** revendiquée dans ADR-013. **Solution retenue** : 1. **Wrapper types Go** avec méthodes `Scan/Value` pour conversion automatique 2. **Utiliser les fonctions PostGIS de conversion** : - `ST_AsGeoJSON()` → struct GeoJSON typée - `ST_AsText()` → string WKT - `geography` brut → `pgtype.Point` (lib pgx) 3. **Documenter le pattern** dans ADR-013 section "Gestion des Types PostGIS" **Action** : - [ ] Créer package `internal/geo` avec wrappers `GeoJSON`, `WKT` - [ ] Mettre à jour ADR-013 section "Types PostGIS" - [ ] Documenter pattern dans README backend --- ### #5 : Cache Redis (TTL 5min) vs Mode Offline (30 jours) | Élément | Détail | |---------|--------| | **ADR concerné** | ADR-005 (BDD, ligne 60) | | **Règle métier** | Règle 11 (Mode Offline, lignes 58-77) | | **Conflit** | Redis avec TTL 5min pour géolocalisation, mais contenu offline valide 30 jours | | **Impact** | En mode offline, impossible de rafraîchir le cache géolocalisation → POI proches non détectés | **Analyse du flux** : ``` Mode connecté: 1. Requête POI proches → Redis (cache 5min) 2. Si miss → PostGIS → Cache Redis 3. ✅ Fonctionne Mode offline (Règle 11): 1. Requête POI proches → Redis (expiré depuis 6 min) 2. Impossible de requêter PostGIS (pas de réseau) ❌ 3. Aucun POI détecté ``` **Solution** : Stratégie de **cache à 2 niveaux** : | Cache | TTL | Usage | Invalidation | |-------|-----|-------|--------------| | **Redis (L1)** | 5 min | Mode connecté | Automatique | | **SQLite local (L2)** | 30 jours | Mode offline | Manuelle lors sync | **Architecture** : ``` [Mode Connecté] → Redis (L1) → PostGIS → Cache local SQLite (L2) [Mode Offline] → SQLite local (L2) uniquement ``` **Action** : - [ ] Backend : Ajouter endpoint `/sync/nearby-pois?lat=X&lon=Y&radius=10km` - [ ] Mobile : Créer `OfflineCacheService` avec SQLite + index spatial - [ ] Mettre à jour ADR-005 section "Cache" avec stratégie 2 niveaux - [ ] Règle 11 : Clarifier sync automatique vs manuel --- ### #6 : Package Geofencing vs Permissions iOS/Android | Élément | Détail | |---------|--------| | **ADR concerné** | ADR-014 (Frontend Mobile, ligne 48) | | **Règle métier** | Règle 05 (lignes 86-134), Règle 11 (RGPD, lignes 51-86) | | **Conflit** | Package `geofence_service` choisi, mais pas de doc sur compatibilité permissions "optionnelles" | | **Impact** | Risque de rejet App Store/Play Store si permissions obligatoires mal gérées | **Problématiques** : 1. **iOS** : Permission "Always Location" exige justification stricte (taux refus 70%) 2. **Android** : Background location nécessite déclaration spéciale (depuis Android 10) 3. **Règle métier** : Permissions optionnelles (app utilisable sans "Always Location") **Package `geofence_service`** : - ✅ Supporte iOS/Android - ⚠️ Documentation peu claire sur permissions optionnelles - ⚠️ Pas de fallback natif si permission refusée **Solution** : **Stratégie de permissions progressive** : ```dart enum LocationPermissionLevel { denied, // Pas de permission whenInUse, // "Quand l'app est ouverte" (iOS) always, // "Toujours" (iOS) / Background (Android) } class GeofencingService { Future requestPermissions() async { // Étape 1: Demander "When In Use" (moins intrusif) var status = await Permission.locationWhenInUse.request(); if (status.isGranted) { // Mode basique: détection seulement app ouverte _enableBasicGeofencing(); // Étape 2 (optionnelle): Proposer upgrade vers "Always" _showUpgradePermissionDialog(); } } Future upgradeToAlwaysPermission() async { // Demandé seulement si utilisateur veut mode piéton complet await Permission.locationAlways.request(); } } ``` **Actions** : - [ ] Mettre à jour ADR-014 avec stratégie permissions progressive - [ ] Créer doc "Permissions Strategy" dans `/docs/mobile/` - [ ] Tests : Validation rejet App Store (TestFlight beta) --- ## 🟡 Incohérences Modérées (Sprint 3-5) ### #7 : Points vs Pourcentages dans les Jauges | Élément | Détail | |---------|--------| | **ADR concerné** | ADR-010 (Commandes Volant, lignes 15-21) | | **Règle métier** | Règle 03 (Centres d'intérêt, lignes 7-14) | | **Conflit** | ADR dit "+2 **points**", Règle dit "+2**%**" pour même action | | **Impact** | Ambiguïté sur calcul : +2 points absolus ou +2% relatifs ? | **Exemple du conflit** : - **ADR-010 (ligne 18)** : "≥80% d'écoute = +2 **points**" - **Règle 03 (ligne 9)** : "≥80% d'écoute = +2**%** à la jauge" **Scénario** : ``` Jauge "Automobile" = 45% Utilisateur écoute 85% d'un podcast voiture Option A (points absolus): 45 + 2 = 47% Option B (pourcentage relatif): 45 * 1.02 = 45.9% ``` **Recommandation** : **Option A (points absolus)** pour simplicité **Justification** : - Progression linéaire plus intuitive - Évite effet "rich get richer" (jauges hautes progressent + vite) - Cohérent avec système de gamification classique **Actions** : - [ ] Clarifier ADR-010 : remplacer "points" par "points de pourcentage" - [ ] Clarifier Règle 03 : uniformiser terminologie - [ ] Backend : Documenter formule exacte dans code --- ### #8 : OAuth2 Complexe vs Email/Password Simple | Élément | Détail | |---------|--------| | **ADR concerné** | ADR-008 (Auth, lignes 12, 52-68) | | **Règle métier** | Règle 01 (Auth, lignes 5-10) | | **Conflit** | ADR implémente OAuth2 PKCE complet, mais Règle dit "❌ Pas d'OAuth tiers, email/password uniquement" | | **Impact** | Sur-ingénierie : OAuth2 conçu pour tiers (Google, Facebook) mais non utilisé ici | **Analyse** : - **ADR-008** : Architecture OAuth2 avec PKCE, refresh tokens, etc. - **Règle 01** : "❌ Pas de Google, Apple, Facebook OAuth" **Zitadel supporte** : - OAuth2 (pour intégrations tierces) - Email/Password natif (ce dont on a besoin) **Question** : Pourquoi implémenter OAuth2 si pas de tiers ? **Options** : | Option | Complexité | Justification | |--------|------------|---------------| | **A. Garder OAuth2** | Haute | Future-proof pour API partenaires | | **B. Session simple** | Basse | Suffit pour MVP email/password | **Recommandation** : **Option A** (garder OAuth2) si : - Vision long-terme : API pour partenaires (créateurs, annonceurs) - Coût marginal : Zitadel gère OAuth2 nativement Sinon **Option B** (session simple) si MVP pur. **Actions** : - [ ] Décision : Confirmer besoin OAuth2 avec product owner - [ ] Si A : Mettre à jour Règle 01 "OAuth tiers en Phase 2" - [ ] Si B : Simplifier ADR-008 (session JWT classique) --- ### #9 : GeoIP Database (MaxMind) | Élément | Détail | |---------|--------| | **ADR concerné** | ADR-005 (non mentionné) | | **Règle métier** | Règle 02 (RGPD, lignes 146-149) | | **Conflit** | Règle cite "MaxMind GeoLite2 (gratuit)", mais offre a changé en 2019 | | **Impact** | Coût caché : MaxMind nécessite compte + API calls (plus de base offline gratuite) | **Historique** : - **Avant 2019** : GeoLite2 database téléchargeable gratuitement - **Après 2019** : Compte requis + limite 1000 requêtes/jour (gratuit) - **Dépassement** : 0.003$/requête **Utilisation RoadWave** : - Mode dégradé (sans GPS) → GeoIP pour localisation approximative - Estimation : 10% des utilisateurs (1000 users × 10% = 100 requêtes/jour) **Options** : | Option | Coût/mois | Précision | Maintenance | |--------|-----------|-----------|-------------| | **A. MaxMind API** | ~10€ | ±50 km | Nulle | | **B. IP2Location Lite** | Gratuit | ±50 km | Maj mensuelle | | **C. Self-hosted GeoIP** | Gratuit | ±50 km | +2h/mois | **Recommandation** : **Option C** (self-hosted avec IP2Location Lite DB) **Architecture** : ``` [Backend Go] → [GeoIP Service] ↓ [IP2Location SQLite DB] (màj mensuelle via cron) ``` **Actions** : - [ ] Backend : Implémenter service GeoIP avec IP2Location - [ ] DevOps : Cron job màj mensuelle de la DB - [ ] Mettre à jour Règle 02 ligne 147 --- ### #10 : Tests BDD Synchronisés (Backend + Mobile) | Élément | Détail | |---------|--------| | **ADR concernés** | ADR-007 (Tests BDD, lignes 30-68), ADR-015 (Stratégie, lignes 59-62) | | **Règle métier** | Toutes (Gherkin) | | **Conflit** | Features partagées `/features`, step definitions séparées → qui exécute quoi ? | | **Impact** | Risque de divergence backend/mobile si tests pas synchronisés | **Architecture actuelle** : ``` /features/*.feature (partagé) /backend/tests/bdd/ (step definitions Go) /mobile/tests/bdd/ (step definitions Dart) ``` **Question non résolue** : - Un test "Authentification" concerne-t-il backend ET mobile ? - Qui est responsable de l'exécuter ? - Si les implémentations divergent ? **Recommandation** : **Catégoriser les features** ``` /features/ /api/ → Backend uniquement (tests API REST) /ui/ → Mobile uniquement (tests interface) /e2e/ → End-to-end (backend + mobile ensemble) ``` **Exemple** : ```gherkin # features/api/authentication.feature (backend) Scénario: Création de compte via API Étant donné une requête POST /api/v1/auth/register Quand j'envoie email "test@example.com" et password "Pass123!" Alors le statut HTTP est 201 Et la réponse contient un token JWT # features/ui/authentication.feature (mobile) Scénario: Création de compte via interface Étant donné que je suis sur l'écran d'inscription Quand je saisis email "test@example.com" Et je saisis mot de passe "Pass123!" Et je clique sur "S'inscrire" Alors je vois l'écran d'accueil ``` **Actions** : - [ ] Réorganiser `/features` en 3 catégories (api, ui, e2e) - [ ] Mettre à jour ADR-007 avec convention de nommage - [ ] CI/CD : Séparer jobs backend-bdd et mobile-bdd --- ### #11 : 70/30 Split Paiements (Vérification Manquante) | Élément | Détail | |---------|--------| | **ADR concerné** | ADR-009 (Paiement, lignes 32-52) | | **Règle métier** | Règle 18 (Monétisation, non fournie complète) | | **Conflit** | ADR assume 70/30 split sans référence règle métier | | **Impact** | Risque de mauvaise répartition revenus créateurs | **ADR-009 spécifie** : - 70% créateur - 30% plateforme **Question** : Est-ce validé par les règles métier business ? **Actions** : - [ ] Lire Règle 18 (Monétisation Créateurs) complète - [ ] Vérifier si 70/30 correspond aux attentes - [ ] Si divergence : mettre à jour ADR-009 --- ### #12 : Monorepo Path Filters vs Features Partagées | Élément | Détail | |---------|--------| | **ADR concernés** | ADR-016 (Monorepo, ligne 80), ADR-015 (Tests) | | **Règle métier** | N/A (problème CI/CD) | | **Conflit** | Path filters pour éviter rebuild tout, mais features partagées déclenchent tout | | **Impact** | Optimisation CI/CD inefficace | **Problème** : ```yaml # .github/workflows/backend.yml on: push: paths: - 'backend/**' - 'features/**' # ❌ Change sur n'importe quel .feature → rebuild backend ``` **Solution** : Path filters **par catégorie** (suite de #10) ```yaml # .github/workflows/backend.yml on: push: paths: - 'backend/**' - 'features/api/**' # ✅ Seulement features API - 'features/e2e/**' # ✅ E2E impacte backend # .github/workflows/mobile.yml on: push: paths: - 'mobile/**' - 'features/ui/**' # ✅ Seulement features UI - 'features/e2e/**' # ✅ E2E impacte mobile ``` **Actions** : - [ ] Implémenter catégorisation features (dépend de #10) - [ ] Mettre à jour workflows CI/CD - [ ] Mettre à jour ADR-016 avec stratégie path filters --- ### #13 : Coûts Email (Transition Free → Paid) | Élément | Détail | |---------|--------| | **ADR concernés** | ADR-018 (Email, lignes 49-52), ADR-017 (Hébergement) | | **Règle métier** | N/A (économique) | | **Conflit** | ADR cite "gratuit" mais limite 9000 emails/mois → plan transition manquant | | **Impact** | Coût surprise lors de la croissance | **ADR-018 spécifie** : - Brevo gratuit : 300 emails/jour = 9000/mois - Phase MVP : 0-10K utilisateurs **Calcul réaliste** : ``` Emails par utilisateur/mois: - Vérification email: 1 - Reset password: 0.1 (10%) - Notifications (opt-in 30%): 4 - Paiements créateurs (5%): 1 Total: ~2 emails/user/mois (moyenne) 10K users × 2 = 20K emails/mois → dépassement tier gratuit ``` **Coût Brevo** : - Free: 0-9K emails - Lite: 19€/mois (20K emails) - Business: 49€/mois (50K emails) **Actions** : - [ ] Mettre à jour ADR-018 avec projection coûts - [ ] Implémenter alertes (90% quota atteint) - [ ] Plan B : Self-hosted SMTP (Postfix) si budget serré --- ### #14 : Kubernetes vs VPS MVP | Élément | Détail | |---------|--------| | **ADR concernés** | ADR-017 (Hébergement, ligne 12), ADR-001 (Go, ligne 27) | | **Règle métier** | N/A (infrastructure) | | **Conflit** | ADR-001 justifie Go pour "Kubernetes first-class", mais ADR-017 utilise VPS simple | | **Impact** | Sur-architecture : pourquoi choisir Go pour K8s si pas utilisé ? | **Analyse** : - **ADR-001** : Go choisi notamment pour "excellent support Kubernetes" - **ADR-017** : MVP sur OVH VPS Essential (single VM, Docker Compose) - **ADR-012** : Mentionne migration K8s "à 1M+ users" **Question** : Justification K8s prématurée ? **Réponse** : **Non, acceptable** si : - Vision long-terme claire (1M users = besoin K8s) - Go apporte autres avantages (perf, concurrence, typing) - Coût marginal (Go vs Node.js comparable en complexité MVP) **Recommandation** : **Clarifier la vision** dans ADR **Actions** : - [ ] Mettre à jour ADR-001 : "Go pour scalabilité future (K8s), mais aussi perf/typage" - [ ] ADR-017 : Ajouter section "Roadmap Infrastructure" (VPS → K8s) --- ## 🟢 Incohérences Mineures (Clarification) ### #15 : Unlike Manuel sur Contenu Auto-liké | Élément | Détail | |---------|--------| | **ADR concerné** | ADR-010 (ligne 15-21) | | **Règle métier** | Règle 05 (lignes 248-323), Règle 03 (lignes 93-99) | | **Conflit** | Auto-like +2% documenté, mais unlike manuel non spécifié | | **Impact** | Ambiguïté : faut-il annuler (+2%) si unlike ? | **Scénario** : ``` 1. Utilisateur écoute 85% → auto-like → jauge +2% 2. Utilisateur clique "Unlike" (toggle) 3. Que se passe-t-il ? Option A: Jauge -2% (annulation) Option B: Jauge reste (unlike n'affecte pas) ``` **Recommandation** : **Option A** (annulation symétrique) **Justification** : Unlike explicite = signal fort "pas intéressé" **Actions** : - [ ] Clarifier Règle 03 : section "Unlike Manuel" - [ ] Backend : Implémenter logique annulation dans `GaugeService` --- ## Plan d'Action Global ### Phase 1 : Résolutions Critiques (Avant Implémentation) | # | Tâche | Responsable | Effort | Deadline | |---|-------|-------------|--------|----------| | 1 | ✅ Créer ADR-019 (Notifications) | Architecture | 2h | ✅ Fait | | 2 | ✅ Mettre à jour ADR-002 (Pre-buffering) | Architecture | 1h | ✅ Fait | | 3 | Implémenter WebSocket backend | Backend Lead | 3j | Sprint 1 | | 4 | Implémenter Pre-buffer mobile | Mobile Lead | 2j | Sprint 1 | ### Phase 2 : Résolutions Importantes (Sprint 1-2) | # | Tâche | Responsable | Effort | Statut | |---|-------|-------------|--------|--------| | 5 | ✅ Décision souveraineté (Zitadel self-host) | CTO | 1h | ✅ **Fait** | | 6 | Package geo types (PostGIS) | Backend | 1j | ⏳ Sprint 2 | | 7 | Cache 2 niveaux (Redis + SQLite) | Backend + Mobile | 3j | ⏳ Sprint 2 | | 8 | Stratégie permissions progressive | Mobile | 2j | ⏳ Sprint 2 | ### Phase 3 : Résolutions Modérées (Sprint 3-5) | # | Tâche | Responsable | Effort | Deadline | |---|-------|-------------|--------|----------| | 9-15 | Clarifications ADR/Règles | Tech Writer | 5j | Sprint 3-4 | | 16 | Réorganisation features BDD | QA Lead | 2j | Sprint 4 | | 17 | Optimisation CI/CD path filters | DevOps | 1j | Sprint 5 | --- ## Métriques de Suivi | Métrique | Valeur Initiale | Cible | Actuel | |----------|----------------|-------|--------| | Incohérences CRITICAL | 2 | 0 | ✅ **0** (2/2 résolues) | | Incohérences HIGH | 4 | 0 | ⏳ **3** (1/4 résolue) | | Incohérences MODERATE | 8 | ≤2 | ⏳ 8 | | ADR à jour | 66% (12/18) | 100% | ⏳ 78% (14/18) | | Coverage documentation | N/A | >90% | ⏳ 80% | **Dernière mise à jour** : 2026-01-30 --- ## Contacts et Ressources - **Analyse complète** : Ce document - **ADR-019** : `/docs/adr/019-notifications-geolocalisees.md` - **ADR-002 (mis à jour)** : `/docs/adr/002-protocole-streaming.md` - **Questions** : Créer une issue GitHub avec tag `[architecture]` --- **Prochaine revue** : 2026-02-15 (après Sprint 2)