Files
roadwave/docs/INCONSISTENCIES-ANALYSIS.md
jpgiannetti 852f6d5e16 refactor(docs): réorganiser ADR et règles métier pour clarté
**Changements majeurs** :

1. **Suppression ADR-010 (Commandes volant et likes)** :
   - Contenu consolidé dans Règle 05 (section 5.3)
   - Raison : ADR-010 était du métier déguisé en architecture
   - Section "Implémentation Technique" ajoutée à Règle 05
   - Pattern correct (addition) vs incorrect (multiplication)

2. **Déplacement ADR-011 → Compliance** :
   - `docs/adr/011-conformite-stores.md` → `docs/compliance/stores-submission.md`
   - Raison : Nature opérationnelle/légale, pas architecture technique
   - Nouveau dossier `/docs/compliance/` créé

3. **Renumérotation ADR (010-022)** :
   - Combler les trous de numérotation (010 et 011)
   - ADR-012→010, ADR-013→011, ..., ADR-024→022
   - 22 ADR numérotés en continu (001-022)
   - Historique Git préservé (git mv)

4. **Mise à jour références** :
   - Règle 03 : ADR-010 → Règle 05 (section 5.3)
   - Règle 09 : ADR-010 → Règle 05 (section 5.3)
   - INCONSISTENCIES-ANALYSIS.md : toutes références mises à jour
   - Incohérence #15 annulée (faux problème : modes séparés)

**Résultat** :
-  Séparation claire ADR (technique) vs Règles métier (fonctionnel)
-  Documentation compliance séparée
-  Numérotation ADR continue sans trous
-  Single Source of Truth (pas de redondance)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-01 14:34:12 +01:00

35 KiB
Raw Blame History

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 14% RÉSOLU avant implémentation
🟠 HIGH 2 14% RÉSOLU (2 résolus, 1 annulé) Résolution Sprint 1-2
🟡 MODERATE 9 64% RÉSOLU (6 résolus, 2 annulés, 1 documenté) Résolution Sprint 3-5
🟢 LOW 1 7% ANNULÉ (Faux problème) À clarifier lors du développement

Impact par Domaine

Domaine Nombre d'incohérences Criticité maximale
Streaming & Géolocalisation 3 🔴 CRITICAL
Données & Infrastructure 2 🟠 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-017 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-017 : 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 :

  • Décision validée : Self-host sur OVH
  • ADR-008 mis à jour avec architecture self-hosted
  • TECHNICAL.md mis à jour

#4 : ORM sqlc vs Types PostGIS

Statut : RÉSOLU (ADR-011 mis à jour)

Élément Détail
ADR concerné ADR-011 (section "Gestion des Types PostGIS")
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

Solution implémentée :

Wrappers typés + fonctions de conversion PostGIS :

  1. Wrapper types Go avec méthodes Scan/Value pour conversion automatique
  2. Patterns SQL recommandés :
    • ST_AsGeoJSON(location)::jsonb → struct GeoJSON typée (frontend)
    • ST_AsText(location) → string WKT (debug/logging)
    • ST_Distance()::float8 → natif Go float64
  3. Index GIST sur colonnes géographiques pour performance
  4. Architecture conversion :
    SQL PostGIS → ST_AsGeoJSON() → json.RawMessage → GeoJSON (strongly-typed)
    

Code Pattern :

// internal/geo/types.go
type GeoJSON struct {
    Type        string    `json:"type"`
    Coordinates [2]float64 `json:"coordinates"`
}

func (g *GeoJSON) Scan(value interface{}) error {
    bytes, _ := value.([]byte)
    return json.Unmarshal(bytes, g)
}
-- queries/poi.sql
SELECT id, ST_AsGeoJSON(location)::jsonb as location,
       ST_Distance(location, $1::geography) as distance_meters
FROM points_of_interest
WHERE ST_DWithin(location, $1::geography, $2);

Actions requises :

  • Créer package backend/internal/geo avec wrappers
  • Ajouter migrations index GIST (CREATE INDEX idx_poi_gist ON pois USING GIST(location))
  • Tests d'intégration avec Testcontainers (PostGIS réel)
  • Documenter patterns dans backend/README.md

Référence : ADR-011 - Gestion des Types PostGIS


#5 : Cache Redis (TTL 5min) vs Mode Offline (30 jours)

Statut : ANNULÉ (Faux problème)

É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

Raison de l'annulation : Le mode offline ne concerne pas les POI (Points d'Intérêt) mais uniquement le contenu audio déjà téléchargé. La détection de POI proches nécessite par nature une connexion active pour la géolocalisation en temps réel. Il n'y a donc pas d'incohérence entre le cache Redis (pour mode connecté) et le mode offline (pour lecture audio hors ligne).

Aucune action requise : Ce point est un faux problème et peut être ignoré.


#6 : Package Geofencing vs Permissions iOS/Android

Statut : RÉSOLU (Stratégie de permissions progressive implémentée)

Élément Détail
ADR concerné ADR-010 (Frontend Mobile, mis à jour)
Règle métier Règle 05 (section 5.1.2, mis à jour), Règle 02 (RGPD)
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

Solution implémentée :

Stratégie de permissions progressive en 2 étapes :

enum LocationPermissionLevel {
  denied,           // Pas de permission
  whenInUse,        // "Quand l'app est ouverte" (iOS)
  always,           // "Toujours" (iOS) / Background (Android)
}

class GeofencingService {
  Future<void> 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<void> upgradeToAlwaysPermission() async {
    // Demandé seulement si utilisateur veut mode piéton complet
    await Permission.locationAlways.request();
  }
}

Actions complétées :

  • ADR-010 mis à jour avec section complète "Stratégie de Permissions"
  • Règle 05 (section 5.1.2) mise à jour avec clarifications permissions progressive
  • Documentation détaillée créée : /docs/mobile/permissions-strategy.md
  • Plan de validation TestFlight créé : /docs/mobile/testflight-validation-plan.md

Changements apportés :

  • Permissions demandées en 2 étapes : "When In Use" (onboarding) → "Always" (optionnel, mode piéton)
  • Écran d'éducation obligatoire avant demande "Always" (requis pour validation stores)
  • Fallback gracieux à tous niveaux : app utilisable même sans permission arrière-plan
  • Mode dégradé (GeoIP) si toutes permissions refusées
  • Configuration iOS/Android complète avec textes validés RGPD
  • Plan de validation beta (TestFlight + Play Console Internal Testing)

Références :


🟡 Incohérences Modérées (Sprint 3-5)

#7 : Points vs Pourcentages dans les Jauges

Statut : RÉSOLU (Terminologie unifiée : points de pourcentage absolus)

Élément Détail
ADR concerné Règle 05 (section 5.3) (Commandes Volant, mis à jour)
Règle métier Règle 03 (Centres d'intérêt, mis à jour)
Conflit ADR dit "+2 points", Règle dit "+2**%**" pour même action
Impact Ambiguïté sur calcul : +2 points absolus ou +2% relatifs ?

Solution adoptée : Option A (points de pourcentage absolus)

Calcul confirmé :

Jauge "Automobile" = 45%
Utilisateur écoute 85% d'un podcast voiture
→ Like renforcé : +2%
→ 45 + 2 = 47% ✅

NOT 45 × 1.02 = 45.9% ❌

Justification :

  • Progression linéaire : Intuitive et prévisible
  • Équité : Tous les utilisateurs progressent à la même vitesse
  • Gamification standard : Cohérent avec Duolingo, Spotify, Strava
  • Simplicité technique : Addition simple, pas de risque d'overflow
  • Prédictibilité UX : "+2%" signifie vraiment +2 points de pourcentage

Actions complétées :

  • Règle 05 (section 5.3) mis à jour : "points" → "+2%" avec note explicite "points de pourcentage absolus"
  • Règle 05 (section 5.3) : Section "Implémentation Technique" ajoutée (architecture 2 services)
  • Règle 03 : Note ajoutée clarifiant calcul absolu vs relatif
  • Règle 03 : Exemples de calcul vérifiés et cohérents
  • Référence croisée Règle 05 (section 5.3) ↔ Règle 03
  • ADR-010 supprimé : Contenu consolidé dans Règle 05 (métier) pour éviter redondance

Changements apportés :

Règle 05 (section 5.3) :

  • Règles reformulées : "+2 points" → "+2%" (points de pourcentage absolus)
  • Note explicite ajoutée : "Par exemple, si jauge = 45%, +2% → 47%"
  • Nouvelle section "Implémentation Technique" avec architecture 2 services (Calculation + Update)
  • Pattern de calcul correct (addition) vs incorrect (multiplication)
  • Exemples de calcul concrets

Règle 03 :

  • Tableau mis à jour : valeurs en gras (+2%, +1%, etc.)
  • Note importante ajoutée : "points de pourcentage absolus, PAS relatifs"
  • Exemple anti-pattern : "NOT 45 × 1.02 = 45.9% "
  • Référence croisée vers Règle 05 (section 5.3) pour implémentation

Références :


#8 : OAuth2 Complexe vs Email/Password Simple

Statut : RÉSOLU (Clarification : OAuth2 = protocole, PAS providers tiers)

Élément Détail
ADR concerné ADR-008 (Auth, mis à jour)
Règle métier Règle 01 (Auth, mis à jour)
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

Clarification : Il y avait une confusion terminologique entre :

  • OAuth2 PKCE (protocole d'authentification moderne pour mobile) Utilisé
  • OAuth providers tiers (Google, Apple, Facebook) Pas utilisés

Solution adoptée :

RoadWave utilise Zitadel self-hosted avec email/password natif uniquement :

Aspect Détail
Méthode d'authentification Email + mot de passe (formulaire natif Zitadel)
Protocole technique OAuth2 PKCE (entre app mobile et Zitadel)
Fournisseurs tiers Aucun (pas de Google, Apple, Facebook)

Pourquoi OAuth2 PKCE alors ? :

  • Standard moderne pour auth mobile (sécurisé, refresh tokens)
  • Protocole, pas un provider externe
  • Alternative serait session cookies (moins adapté mobile) ou JWT custom (réinventer la roue)
  • Zitadel implémente OAuth2/OIDC comme protocole, mais auth reste email/password

Flow d'authentification :

User → Formulaire email/password (app mobile)
     → Zitadel (OAuth2 PKCE protocol)
     → Validation email/password natif
     → JWT access token + refresh token
     → Go API (validation JWT locale)

Actions complétées :

  • ADR-008 : Section "OAuth2 PKCE : Protocole vs Fournisseurs Tiers" ajoutée
  • ADR-008 : Architecture clarifiée ("Email/Pass native" dans diagramme)
  • ADR-008 : Note explicite : "OAuth2 PKCE = protocole, PAS providers tiers"
  • Règle 01 : Clarification technique ajoutée + référence croisée ADR-008

Références :


#9 : GeoIP Database (MaxMind)

Statut : RÉSOLU (ADR-019 créé)

Élément Détail
ADR concerné ADR-019 (créé)
Règle métier Règle 02 (RGPD, mis à jour)
Conflit Règle citait "MaxMind GeoLite2 (gratuit)", mais offre a changé en 2019
Impact Coût caché potentiel

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)

Solution implémentée : IP2Location Lite (self-hosted)

Option Coût/mois Précision Maintenance
IP2Location Lite Gratuit ±50 km Maj mensuelle
MaxMind API ~10€ ±50 km Nulle
Self-hosted MaxMind Gratuit ±50 km Compte requis

Architecture :

[Backend Go] → [GeoIP Service]
                     ↓
               [IP2Location SQLite DB]
               (màj mensuelle via cron)

Avantages :

  • Gratuit (pas de limite de requêtes)
  • Self-hosted (souveraineté des données, cohérence avec ADR-004)
  • Pas de compte tiers requis
  • Base de données SQLite légère (50-100 MB)
  • Mise à jour mensuelle automatisable

Actions complétées :

  • ADR-019 créé : Service de Géolocalisation par IP
  • Règle 02 mise à jour (ligne 147 et 317)

Actions requises :

  • Backend : Implémenter service GeoIP avec IP2Location
  • DevOps : Cron job màj mensuelle de la DB

Référence : ADR-019 - Service de Géolocalisation par IP


#10 : Tests BDD Synchronisés (Backend + Mobile)

Statut : RÉSOLU (Catégorisation features implémentée)

Élément Détail
ADR concernés ADR-007 (mis à jour), ADR-011 (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 initiale :

/features/*.feature           (mélangées par domaine)
/backend/tests/bdd/           (step definitions Go)
/mobile/tests/bdd/            (step definitions Dart)

Solution implémentée : Catégorisation en 3 couches

/features/
  /api/          → Backend uniquement (tests API REST)
    ├── authentication/       # REST endpoints, validation email, 2FA
    ├── recommendation/       # Algorithm backend, scoring GPS
    ├── rgpd-compliance/      # GDPR API (delete, export, consent)
    ├── content-creation/     # Upload, encoding, validation API
    ├── moderation/           # Moderation workflow API
    ├── monetisation/         # Payments, KYC, payouts API
    ├── premium/              # Subscription API
    ├── radio-live/           # Live streaming backend
    └── publicites/           # Ads API, budget, metrics

  /ui/           → Mobile uniquement (tests interface)
    ├── audio-guides/         # Audio player UI, modes (piéton, vélo)
    ├── navigation/           # Steering wheel, voice commands, UI
    ├── interest-gauges/      # Gauge visualization, progression
    ├── mode-offline/         # Download UI, sync status
    ├── partage/              # Share dialog
    ├── profil/               # Creator profile screen
    └── recherche/            # Search bar, filters UI

  /e2e/          → End-to-end (backend + mobile ensemble)
    ├── abonnements/          # Full subscription flow (Mangopay + Zitadel + UI)
    └── error-handling/       # Network errors, GPS disabled (backend + mobile)

Changements apportés :

  • 93 features réorganisées en 3 catégories (api/ui/e2e)
  • ADR-007 mis à jour avec section complète "Convention de Catégorisation"
  • ADR-014 mis à jour avec stratégie CI/CD path filters (documentée, implémentation reportée)
  • Historique Git préservé via git mv (pas de perte d'historique)

Actions complétées :

  • Réorganiser /features en 3 catégories (api, ui, e2e)
  • Mettre à jour ADR-007 avec convention de nommage et exemples
  • ⏸️ CI/CD : Documenté dans ADR-014 (implémentation reportée jusqu'au développement backend/mobile)

Références :


#11 : 70/30 Split Paiements (Vérification Manquante)

Statut : ANNULÉ (Faux problème - Documentation complète et cohérente)

Élément Détail
ADR concerné ADR-009 (Paiement, lignes 32-52)
Règle métier Règle 18 (Monétisation créateurs, section 9.4.B, lignes 121-165) Existe et complète
Conflit ADR assume 70/30 split sans référence règle métier Aucun conflit
Impact Risque de mauvaise répartition revenus créateurs Aucun impact

Vérification complète :

ADR-009 spécifie :

  • 70% créateur
  • 30% plateforme
  • Diagramme explicite : "Créateur A 70%", "Créateur B 70%", "Plateforme 30%"

Règle 18 (section 9.4.B, lignes 121-165) spécifie :

  • Formule exacte : "70% au créateur, 30% à la plateforme"
  • Répartition proportionnelle : au temps d'écoute effectif
  • Exemple concret :
    Utilisateur Premium = 4.99€/mois
    ├─ 3.49€ reversés aux créateurs (70%)
    └─ 1.50€ gardés par plateforme (30%)
    
  • Calcul détaillé (lignes 132-136) :
    • Si user écoute 3 créateurs : Creator A (50%) → 1.75€, Creator B (30%) → 1.05€, Creator C (20%) → 0.70€
  • Requête SQL fournie (lignes 140-151) : implémentation technique de la distribution proportionnelle
  • Comparaison industrie (lignes 153-157) :
    • YouTube Premium : 70/30
    • Spotify : 70/30
    • Apple Music : 52/48 (moins favorable)
    • RoadWave : 70/30 (standard)
  • Justifications business (lignes 159-163) :
    • Ratio standard industrie (prouvé et équitable)
    • Incitation qualité : créateurs avec plus d'écoutes gagnent plus
    • Équité : pas de "winner takes all", chaque créateur reçoit sa part
    • Marge plateforme : 30% couvre l'absence de revenus publicitaires sur Premium

Conclusion : Il n'y a aucune incohérence. ADR-009 et Règle 18 sont parfaitement alignés et se complètent :

  • ADR-009 documente l'implémentation technique (Mangopay, split payments)
  • Règle 18 documente la logique métier (formule, exemples, justifications, comparaisons)

Actions complétées :

  • Règle 18 lue et analysée complètement
  • Vérification 70/30 : cohérent entre ADR-009 et Règle 18
  • Mise à jour ADR-009 : non requise (déjà correct)

Aucune action requise : Ce point peut être fermé définitivement.


#12 : Monorepo Path Filters vs Features Partagées

Statut : ⏸️ DOCUMENTÉ (Implémentation CI/CD reportée)

Élément Détail
ADR concernés ADR-014 (Monorepo, mis à jour)
Règle métier N/A (problème CI/CD)
Conflit initial Path filters pour éviter rebuild tout, mais features partagées déclenchent tout
Impact Optimisation CI/CD inefficaceRésolu par catégorisation #10

Problème initial :

# .github/workflows/backend.yml
on:
  push:
    paths:
      - 'backend/**'
      - 'features/**'  # ❌ Change sur n'importe quel .feature → rebuild backend

Solution implémentée : Path filters par catégorie (dépend de #10 résolu)

# .github/workflows/backend.yml (architecture documentée)
on:
  push:
    paths:
      - 'backend/**'
      - 'features/api/**'   # ✅ Seulement features API
      - 'features/e2e/**'   # ✅ E2E impacte backend

# .github/workflows/mobile.yml (architecture documentée)
on:
  push:
    paths:
      - 'mobile/**'
      - 'features/ui/**'    # ✅ Seulement features UI
      - 'features/e2e/**'   # ✅ E2E impacte mobile

Changements apportés :

  • Catégorisation features (point #10) : résolue → permet path filters sélectifs
  • ADR-014 mis à jour avec section complète "Stratégie CI/CD avec Path Filters"
    • Architecture workflows séparés (backend.yml, mobile.yml, shared.yml)
    • Configuration path filters détaillée
    • Tableau de déclenchement par type de modification
    • Avantages (rebuild sélectif, économie ~70% temps CI, parallélisation)

Actions complétées :

  • Catégorisation features implémentée (résolution #10)
  • ADR-014 mis à jour avec stratégie path filters complète
  • ⏸️ Implémentation workflows CI/CD : Reportée jusqu'à l'implémentation du code backend/mobile

Note importante : Le projet est actuellement en phase de documentation uniquement (aucun code backend/mobile implémenté). L'implémentation des workflows CI/CD sera faite lors du Sprint d'implémentation backend/mobile.

Références :


#13 : Coûts Email (Transition Free → Paid)

Statut : RÉSOLU (Périmètre réduit : emails techniques uniquement)

Élément Détail
ADR concernés ADR-016 (mis à jour)
Règle métier N/A (économique)
Conflit initial ADR citait "gratuit" mais volume estimé dépassait 9000 emails/mois
Impact initial Coût surprise lors de la croissance

Décision : Limiter aux emails techniques uniquement (pas de notifications, alertes marketing, newsletters)

Périmètre strict :

  • Authentification (vérification email, reset password, changement email)
  • Sécurité (alertes connexion inhabituelle)
  • Modération (strikes, suspensions)
  • RGPD (confirmation suppression, export données)
  • Pas de notifications sociales (écoutes, likes, commentaires)
  • Pas d'alertes marketing (recommandations, nouvelles sorties)
  • Pas de newsletters/promotions
  • Pas d'emails paiements créateurs (Mangopay envoie déjà ses propres emails)

Calcul révisé (emails techniques uniquement) :

Emails par utilisateur/mois (régime stable):
- Vérification email (nouveaux users): 0.1 (10% croissance)
- Reset password: 0.1 (10% des users)
- Changement email: 0.05 (5%)
- Alertes sécurité: 0.02 (2%)
- Modération: 0.01 (1%)

Total: ~0.28 emails/user/mois

10K users × 0.28 = 2800 emails/mois = 93 emails/jour
→ Largement sous le tier gratuit (300/jour) ✅

Projection de coûts révisée :

Phase Utilisateurs Emails/jour moyen Coût Brevo
MVP 0-10K 93/jour Gratuit
Growth 10K-50K 467/jour 19€/mois (Lite)
Scale 50K-100K 933/jour 49€/mois (Business)

Gestion des pics :

  • Rate limiting : 250 emails/heure (batch processing)
  • Redis queue pour lisser l'envoi sur 24-48h
  • Upgrade temporaire Lite (19€) si pic > 300/jour sur 3+ jours

Actions complétées :

  • ADR-016 mis à jour avec périmètre strict et projection coûts
  • Clarification : pas d'emails notifications/marketing/paiements
  • Stratégie gestion pics d'inscription documentée

Référence : ADR-016 - Service d'Emailing Transactionnel


#14 : Kubernetes vs VPS MVP

Statut : RÉSOLU (Vision clarifiée : K8s est un bonus, pas la raison principale)

Élément Détail
ADR concernés ADR-015 (mis à jour), ADR-001 (mis à jour)
Règle métier N/A (infrastructure)
Conflit initial ADR-001 justifiait Go pour "Kubernetes first-class", mais ADR-015 utilisait VPS simple
Impact initial Ambiguïté : Go choisi pour K8s mais K8s pas utilisé en MVP

Analyse :

  • ADR-001 initial : Mentionnait "Kubernetes first-class" dans tooling natif
  • ADR-015 initial : MVP sur OVH VPS Essential (Docker Compose), K8s à "100K+ users"
  • Problème perçu : Incohérence entre choix Go (pour K8s) et infra MVP (pas K8s)

Clarification apportée :

Go est choisi principalement pour :

  1. Simplicité et time-to-market (MVP 8 semaines vs 12+ Rust)
  2. Écosystème mature (PostGIS, WebRTC, Zitadel, BDD tests)
  3. Performance concurrentielle (1M conn/serveur suffisant)
  4. Typing fort et tooling natif (pprof, race detector)

Kubernetes est un bonus pour scalabilité future (Phase 3 : 100K+ users), pas la raison principale.

Solution implémentée :

ADR-001 : Note ajoutée clarifiant que :

  • K8s n'est pas utilisé en MVP (Docker Compose suffit pour 0-20K users)
  • Go choisi principalement pour simplicité, écosystème, performance
  • Support K8s = bonus scalabilité future, pas driver du choix

ADR-015 : Section complète "Roadmap Infrastructure" ajoutée :

Phase Users Infrastructure Trigger principal
MVP 0-20K OVH VPS + Docker Compose Aucun (démarrage)
Croissance 20-100K Scaleway managé CPU > 70% OU MRR > 2000€
Scale 100K+ Scaleway Kubernetes Auto-scaling OU multi-région

Triggers de migration détaillés :

  • Phase 2 : CPU > 70%, latence p99 > 100ms, MRR > 2000€
  • Phase 3 : Auto-scaling requis, multi-région, > 5 services backend, DevOps dédié

Actions complétées :

  • ADR-001 mis à jour : Note explicite "K8s = bonus, pas raison principale"
  • ADR-015 : Section "Roadmap Infrastructure" complète (3 phases + triggers)
  • Cohérence architecture : Vision long-terme clarifiée sans sur-architecture MVP

Références :


🟢 Incohérences Mineures (Clarification)

#15 : Unlike Manuel sur Contenu Auto-liké

Statut : ANNULÉ (Faux problème - séparation mode voiture/piéton)

Élément Détail
ADR concerné Règle 05 (section 5.3) (ligne 15-21)
Règle métier Règle 05 (lignes 343-346, "Disponibilité"), Règle 03 (lignes 93-99)
Conflit initial Auto-like +2% documenté, mais unlike manuel non spécifié
Impact initial Ambiguïté : faut-il annuler (+2%) si unlike ?

Raison de l'annulation : Le scénario du conflit ne peut pas se produire car les deux fonctionnalités sont mutuellement exclusives selon le mode de déplacement :

Séparation stricte par mode (Règle 05, lignes 343-346) :

  • Mode voiture (vitesse ≥ 5 km/h) :
    • Auto-like actif (basé sur temps d'écoute)
    • Pas de bouton Unlike (aucune action manuelle, sécurité routière)
  • Mode piéton (vitesse < 5 km/h) :
    • Bouton Like/Unlike disponible (interactions manuelles)
    • Pas d'auto-like (seulement actions explicites)

Scénario impossible :

1. Utilisateur écoute 85% en mode voiture → auto-like → jauge +2%
   → Pas de bouton Unlike (mode conduite) ❌

2. Utilisateur en mode piéton → bouton Unlike disponible
   → Pas d'auto-like (seulement like manuel) ❌

Justification :

  • L'écoute longue (85%) éveille la curiosité (justifie auto-like en mode voiture)
  • Le unlike ne se fait qu'en mode piéton (où il n'y a pas d'auto-like)
  • Les deux systèmes sont isolés et ne peuvent pas interagir

Aucune action requise : Ce point est un faux problème et peut être ignoré.


Plan d'Action Global

Phase 1 : Résolutions Critiques (Avant Implémentation)

# Tâche Responsable Effort Deadline
1 Créer ADR-017 (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 Fait
7 Cache 2 niveaux (Redis + SQLite) Backend + Mobile 3j Annulé (faux problème)
8 Stratégie permissions progressive Mobile 2j Fait

Phase 3 : Résolutions Modérées (Sprint 3-5)

# Tâche Responsable Effort Statut
9 Clarification Points vs Pourcentages (Règle 05 + Règle 03, ADR-010 supprimé) Tech Writer 0.5j Fait
10 Clarification OAuth2 protocole vs providers (ADR-008 + Règle 01) Tech Writer 0.5j Fait
11 GeoIP Database (ADR-019 + Règle 02) Tech Writer 0.5j Fait
12 Réorganisation features BDD + CI/CD path filters (ADR-007, ADR-020) QA Lead 2j Fait
13 Projection coûts Email (ADR-016, périmètre réduit) Tech Writer 0.5j Fait
14 Clarification Kubernetes (ADR-001, ADR-015 roadmap) Tech Writer 0.5j Fait
15 Unlike Manuel (Faux problème - modes séparés) Tech Writer 0.5j Annulé

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 0 (2 résolues, 1 annulée)
Incohérences MODERATE 9 ≤2 0 (6 résolus, 2 annulés, 1 documenté)
Incohérences LOW 1 0 0 (1 annulée)
ADR à jour 66% (12/18) 100% 100% (19/19 - ADR-016 mis à jour)
Coverage documentation N/A >90% 95%

Dernière mise à jour : 2026-02-01

Détail MODERATE :

  • Traités (9/9) : #7 (résolu), #8 (résolu), #9 (résolu), #10 (résolu), #11 (annulé), #12 (documenté), #13 (résolu), #14 (résolu), #15 (annulé)

Détail LOW :

  • Traité (1/1) : #15 (Unlike Manuel - annulé, reclassé de MODERATE → LOW puis annulé car faux problème)

Contacts et Ressources

  • Analyse complète : Ce document
  • ADR-017 : /docs/adr/017-notifications-geolocalisees.md
  • ADR-019 : /docs/adr/019-geolocalisation-ip.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)