Initial commit
This commit is contained in:
671
docs/INCONSISTENCIES-ANALYSIS.md
Normal file
671
docs/INCONSISTENCIES-ANALYSIS.md
Normal file
@@ -0,0 +1,671 @@
|
||||
# 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<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** :
|
||||
- [ ] 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)
|
||||
Reference in New Issue
Block a user