# Documentation RoadWave --- ## Table des matières - [RoadWave](#roadwave) - [RoadWave - Architecture Technique](#roadwave---architecture-technique) - [ADR-001 : Langage Backend](#adr-001--langage-backend) - [ADR-002 : Protocole de Streaming](#adr-002--protocole-de-streaming) - [ADR-003 : Codec Audio](#adr-003--codec-audio) - [ADR-004 : CDN](#adr-004--cdn) - [ADR-005 : Base de Données](#adr-005--base-de-données) - [ADR-006 : Chiffrement](#adr-006--chiffrement) - [ADR-007 : Tests et Spécifications Exécutables](#adr-007--tests-et-spécifications-exécutables) - [ADR-008 : Authentification et Gestion d'Identité](#adr-008--authentification-et-gestion-didentité) - [ADR-009 : Solution de Paiement et Gestion des Abonnements](#adr-009--solution-de-paiement-et-gestion-des-abonnements) - [ADR-010 : Commandes au volant et likes](#adr-010--commandes-au-volant-et-likes) - [ADR-011 : Conformité App Stores et Plateformes Auto](#adr-011--conformité-app-stores-et-plateformes-auto) - [ADR-012 : Architecture Backend](#adr-012--architecture-backend) - [ADR-013 : ORM et Accès Données](#adr-013--orm-et-accès-données) - [ADR-014 : Frontend Mobile](#adr-014--frontend-mobile) - [ADR-015 : Stratégie Tests](#adr-015--stratégie-tests) - [Règles métier RoadWave](#règles-métier-roadwave) - [Annexe : Fonctionnalités reportées Post-MVP](#annexe--fonctionnalités-reportées-post-mvp) - [Audio-guides multi-séquences pour piétons](#audio-guides-multi-séquences-pour-piétons) - [Impact des abonnements sur l'algorithme](#impact-des-abonnements-sur-lalgorithme) - [Limites d'abonnements et désabonnement](#limites-dabonnements-et-désabonnement) - [Notifications contextuelles selon le mode de déplacement](#notifications-contextuelles-selon-le-mode-de-déplacement) - [Création d'audio-guide multi-séquences](#création-daudio-guide-multi-séquences) - [Intégration audio-guides avec autres fonctionnalités](#intégration-audio-guides-avec-autres-fonctionnalités) - [Audio-guide mode piéton (navigation manuelle)](#audio-guide-mode-piéton-navigation-manuelle) - [Audio-guide mode voiture (GPS automatique)](#audio-guide-mode-voiture-gps-automatique) - [Audio-guides modes vélo et transport](#audio-guides-modes-vélo-et-transport) - [Audio-guides Premium et monétisation](#audio-guides-premium-et-monétisation) - [Sauvegarde et reprise de progression audio-guide](#sauvegarde-et-reprise-de-progression-audio-guide) - [Classification des contenus par âge](#classification-des-contenus-par-âge) - [Connexion utilisateur](#connexion-utilisateur) - [Inscription utilisateur](#inscription-utilisateur) - [Récupération de compte](#récupération-de-compte) - [Gestion des sessions et tokens](#gestion-des-sessions-et-tokens) - [Authentification à deux facteurs (2FA)](#authentification-à-deux-facteurs-2fa) - [Vérification d'email](#vérification-demail) - [Métadonnées et publication de contenu](#métadonnées-et-publication-de-contenu) - [Modification et suppression de contenu](#modification-et-suppression-de-contenu) - [Upload et encodage de contenu audio](#upload-et-encodage-de-contenu-audio) - [Validation des 3 premiers contenus](#validation-des-3-premiers-contenus) - [Élargissement automatique de zone quand aucun contenu n'est disponible](#élargissement-automatique-de-zone-quand-aucun-contenu-nest-disponible) - [Gestion d'un contenu supprimé pendant l'écoute](#gestion-dun-contenu-supprimé-pendant-lécoute) - [Mode dégradé sans géolocalisation](#mode-dégradé-sans-géolocalisation) - [Gestion de la perte de réseau et buffering adaptatif](#gestion-de-la-perte-de-réseau-et-buffering-adaptatif) - [Tests BDD - Documentation des fonctionnalités](#tests-bdd---documentation-des-fonctionnalités) - [Pas de dégradation temporelle des jauges](#pas-de-dégradation-temporelle-des-jauges) - [Évolution des jauges d'intérêt](#évolution-des-jauges-dintérêt) - [Jauge initiale et cold start](#jauge-initiale-et-cold-start) - [Synchronisation actions offline](#synchronisation-actions-offline) - [Téléchargement de contenus offline](#téléchargement-de-contenus-offline) - [Validité et renouvellement contenus offline](#validité-et-renouvellement-contenus-offline) - [Modération préventive](#modération-préventive) - [Sanctions et notifications de modération](#sanctions-et-notifications-de-modération) - [Signalement de contenu inapproprié](#signalement-de-contenu-inapproprié) - [Traitement des signalements par l'IA et les modérateurs](#traitement-des-signalements-par-lia-et-les-modérateurs) - [Conditions d'activation de la monétisation](#conditions-dactivation-de-la-monétisation) - [Contenus Premium exclusifs](#contenus-premium-exclusifs) - [Désactivation et suspension monétisation](#désactivation-et-suspension-monétisation) - [KYC et inscription à la monétisation](#kyc-et-inscription-à-la-monétisation) - [Obligations fiscales](#obligations-fiscales) - [Paiement des créateurs](#paiement-des-créateurs) - [Sources de revenus créateurs](#sources-de-revenus-créateurs) - [Actions complémentaires à l'arrêt](#actions-complémentaires-à-larrêt) - [Commande "Précédent"](#commande-précédent) - [Commandes vocales CarPlay et Android Auto](#commandes-vocales-carplay-et-android-auto) - [Commandes au volant et interactions simplifiées](#commandes-au-volant-et-interactions-simplifiées) - [File d'attente et commande "Suivant"](#file-dattente-et-commande-suivant) - [Lecture en boucle et enchaînement automatique](#lecture-en-boucle-et-enchaînement-automatique) - [Partage de contenu](#partage-de-contenu) - [Avantages Premium](#avantages-premium) - [Gestion abonnement Premium](#gestion-abonnement-premium) - [Multi-devices et détection simultanée](#multi-devices-et-détection-simultanée) - [Offre et tarification Premium](#offre-et-tarification-premium) - [Profil créateur](#profil-créateur) - [Création de campagnes publicitaires](#création-de-campagnes-publicitaires) - [Caractéristiques et facturation des publicités](#caractéristiques-et-facturation-des-publicités) - [Gestion du budget et alertes publicitaires](#gestion-du-budget-et-alertes-publicitaires) - [Insertion et fréquence des publicités](#insertion-et-fréquence-des-publicités) - [Métriques d'engagement et dashboard publicitaire](#métriques-dengagement-et-dashboard-publicitaire) - [Validation et modération des publicités](#validation-et-modération-des-publicités) - [Architecture technique radio live](#architecture-technique-radio-live) - [Arrêt du live](#arrêt-du-live) - [Comportement auditeur pendant un live](#comportement-auditeur-pendant-un-live) - [Démarrage d'un live](#démarrage-dun-live) - [Recherche de contenu](#recherche-de-contenu) - [Classification de géo-pertinence des contenus](#classification-de-géo-pertinence-des-contenus) - [Gestion du contenu politique (MVP simplifié)](#gestion-du-contenu-politique-mvp-simplifié) - [Contenus géolocalisés en mode voiture](#contenus-géolocalisés-en-mode-voiture) - [Gestion de l'historique et reproposition](#gestion-de-lhistorique-et-reproposition) - [Médias traditionnels sur RoadWave](#médias-traditionnels-sur-roadwave) - [Mode Kids pour utilisateurs 13-15 ans](#mode-kids-pour-utilisateurs-13-15-ans) - [Paramétrabilité admin et A/B testing](#paramétrabilité-admin-et-ab-testing) - [Paramétrabilité utilisateur et profils](#paramétrabilité-utilisateur-et-profils) - [Formule de scoring et recommandation](#formule-de-scoring-et-recommandation) - [Anonymisation des données GPS après 24h](#anonymisation-des-données-gps-après-24h) - [Conformité administrative RGPD (Registre, Breach, DPO)](#conformité-administrative-rgpd-registre-breach-dpo) - [Gestion du consentement RGPD](#gestion-du-consentement-rgpd) - [Durée de conservation des données et purge automatique](#durée-de-conservation-des-données-et-purge-automatique) - [Cookies et analytics avec Matomo self-hosted](#cookies-et-analytics-avec-matomo-self-hosted) - [Mode dégradé avec GeoIP (sans GPS précis)](#mode-dégradé-avec-geoip-sans-gps-précis) - [Portabilité des données (Article 20 RGPD)](#portabilité-des-données-article-20-rgpd) - [Suppression du compte utilisateur (Article 17 RGPD - Droit à l'effacement)](#suppression-du-compte-utilisateur-article-17-rgpd---droit-à-leffacement) ---
# RoadWave Réseau social audio géolocalisé pour les usagers de la route. ## Concept RoadWave permet aux conducteurs d'écouter du contenu audio contextuel pendant leurs trajets. La navigation se fait par commandes au volant (suivant/précédent), inspirée des réseaux à scroll infini. Le contenu est diffusé en fonction de la position géographique de l'utilisateur et de ses centres d'intérêt. --- ## Cas d'usage | Utilisateur | Scénario | |-------------|----------| | **Conducteur** | Écoute contenu audio en conduisant, navigation par commandes au volant (suivant/précédent), reçoit notifications géolocalisées en passant près de points d'intérêt | | **Routier** | Écoute podcasts et radios live pendant ses trajets longue distance | | **Touriste à pied** | Visite guidée audio d'un musée, monument ou ville : choisit parmi plusieurs guides, navigue entre séquences à son rythme (tactile/vocal), reçoit notification push quand un audio-guide est disponible à proximité | | **Commerçant** | Diffuse une publicité audio ciblée GPS devant son commerce | | **Passionné auto** | Découvre du contenu automobile près de circuits ou concessionnaires | | **Habitant local** | Partage anecdotes ou bons plans géolocalisés dans son quartier | | **Média traditionnel** | Le Monde, Le Parisien diffusent actualités géolocalisées ou nationales | --- ## Utilisateurs Tout utilisateur peut écouter et créer du contenu (rôle flexible). | Rôle | Description | |------|-------------| | **Auditeur** | Écoute, like, s'abonne à des créateurs, signale des contenus | | **Créateur** | Publie du contenu audio géolocalisé (individus, médias traditionnels) | | **Publicitaire** | Diffuse des publicités ciblées géographiquement | | **Modérateur** | Valide et modère les contenus signalés | --- ## Types de contenu | Type | Description | |------|-------------| | **Contenu court** | Audio de quelques secondes à quelques minutes | | **Podcast** | Épisodes plus longs, séries thématiques | | **Radio live** | Diffusion en direct avec synchronisation approximative entre auditeurs | | **Audio-guide** | Visite guidée multiséquence (musée, monument, ville) : plusieurs séquences numérotées, navigation manuelle entre pistes, liste complète visible, guidage vocal entre points d'intérêt | --- ## Géolocalisation Le créateur définit la zone de diffusion de son contenu : | Niveau | Portée | |--------|--------| | **Point GPS** | Rayon précis autour d'une coordonnée | | **Ville** | Diffusion dans une ville | | **Département** | Diffusion départementale | | **Région** | Diffusion régionale | | **Pays** | Diffusion nationale | **Priorité de diffusion** : plus la zone est précise, plus le contenu a de chances d'être diffusé (GPS > ville > département > région > pays). --- ## Algorithme de recommandation Le contenu proposé est calculé via un **score combiné** : - **Proximité géographique** : distance entre l'utilisateur et la zone du contenu - **Pertinence des intérêts** : correspondance avec les centres d'intérêt de l'utilisateur Lorsque plusieurs contenus sont disponibles dans une zone, **seul le plus pertinent est diffusé**. --- ## Centres d'intérêt Chaque utilisateur possède des **jauges d'intérêt** qui évoluent dynamiquement : ### Catégories - Automobile - Voyage - Famille - Amour - Musique - Économie - Cryptomonnaie - Politique - *... (extensible)* ### Évolution des jauges | Action | Effet | |--------|-------| | Temps d'écoute long | Augmente la jauge | | Like | Augmente la jauge | | Abonnement | Augmente fortement la jauge | | Skip rapide | Diminue la jauge | Les créateurs taguent leur contenu avec des centres d'intérêt. L'algorithme privilégie les correspondances mais n'exclut pas les utilisateurs sans correspondance. --- ## Interactions ### Commandes au volant (conduite) Interactions simplifiées pour sécurité routière maximale : | Commande | Action | |----------|--------| | **Suivant** | Passer au contenu suivant | | **Précédent** | Revenir au contenu précédent | | **Play/Pause** | Mettre en pause / reprendre la lecture | **Like automatique** : Le système détecte automatiquement vos préférences selon votre temps d'écoute : - Écoute ≥80% du contenu → Like renforcé (+2 points jauge) - Écoute 30-79% du contenu → Like standard (+1 point jauge) - Skip après <10s → Signal négatif (-0.5 point) > Voir [ADR-010](#docs/adr/010-commandes-volant) pour les détails techniques ### Actions complémentaires (application à l'arrêt) | Action | Description | |--------|-------------| | **Like explicite** | Bouton cœur pour liker manuellement | | **S'abonner** | Suivre un créateur | | **Signaler** | Signaler un contenu inapproprié | | **Unlike** | Retirer un like | --- ## Publicités - Insertion **entre deux contenus** uniquement (jamais d'interruption) - Ciblage géographique : point GPS, ville, département, région ou national - Interface dédiée pour les publicitaires --- ## Radio live - Diffusion en direct par des créateurs - **Buffering** pour garantir une écoute fluide - **Synchronisation approximative** entre les auditeurs (quelques secondes de décalage possible) --- ## Modération Approche hybride combinant participation communautaire, IA et modérateurs dédiés. ### Contenus prohibés | Catégorie | Description | |-----------|-------------| | **Haine et violence** | Incitation à la haine, violence, discrimination | | **Contenu sexuel** | Pornographie ou contenu sexuellement explicite | | **Illégalité** | Apologie du terrorisme, actes criminels | | **Désinformation dangereuse** | Fausses informations sur la santé, sécurité routière | | **Harcèlement** | Menaces, intimidation, doxxing | | **Droits d'auteur** | Violation de propriété intellectuelle | | **Fraude** | Arnaques, escroqueries | ### Rôles de modération | Rôle | Capacités | |------|-----------| | **Auditeur lambda** | Signaler un contenu (1 clic) | | **Auditeur de confiance** | Signalements priorisés après historique positif | | **Modérateur junior** | Traiter signalements simples (spam, contenu évident) | | **Modérateur senior** | Cas complexes, appels, décisions de ban | | **Admin modération** | Définir les règles, superviser l'équipe | ### Flux de modération ``` 1. Auditeur signale → File d'attente 2. IA pré-filtre → Cas évidents traités automatiquement 3. Modérateur junior → Traite 80% des cas restants 4. Modérateur senior → Cas complexes + recours ``` ### Outils de modération automatique | Outil | Fonction | |-------|----------| | **Transcription audio** | Conversion automatique en texte pour analyse | | **Analyse vocale IA** | Détection de ton agressif, cris, insultes | | **Empreinte audio** | Détection de contenus déjà modérés (réupload) | | **Détection droits d'auteur** | Identification automatique de musique protégée | | **Filtrage mots-clés** | Liste noire de termes inappropriés | ### Modération préventive - **Nouveaux créateurs** : validation manuelle des 3 premiers contenus - **Score de confiance** : évolution selon l'historique du créateur - **Publicités** : validation manuelle obligatoire avant diffusion ### Système de strikes | Strike | Sanction | |--------|----------| | **Strike 1** | Avertissement + formation modération | | **Strike 2** | Suspension 7 jours + contenu supprimé | | **Strike 3** | Suspension 30 jours | | **Strike 4** | Ban définitif | - **Réhabilitation** : -1 strike tous les 6 mois sans incident ### Priorisation des signalements | Priorité | Type de contenu | |----------|-----------------| | **CRITIQUE** | Violence, suicide, mise en danger immédiate | | **HAUTE** | Harcèlement, haine, désinformation | | **MOYENNE** | Spam, contenu inapproprié | | **BASSE** | Qualité audio, tags incorrects | ### Transparence et recours - **Notification explicite** lors de suppression (raison détaillée) - **Processus d'appel** : le créateur peut contester une décision - **Délai de traitement** : 48-72h pour les recours - **Historique** : tableau de bord des sanctions pour le créateur ### Modération communautaire - **Utilisateurs de confiance** : signalements priorisés après historique positif - **Récompenses** : badges, réduction premium pour signalements pertinents - Lutte contre les signalements abusifs (sanctions possibles) --- ## Modèle économique ### Offres | Formule | Description | |---------|-------------| | **Gratuit** | Accès complet avec publicités entre les contenus | | **Premium** | Sans publicité + accès aux contenus exclusifs | ### Monétisation créateurs - **Partage des revenus pub** : rémunération basée sur le nombre d'écoutes - **Pourboires** : les auditeurs peuvent faire des dons aux créateurs --- ## Conformité RGPD ### Données collectées | Donnée | Finalité | Base légale | |--------|----------|-------------| | **Position GPS** | Diffusion de contenu géolocalisé | Consentement | | **Historique d'écoute** | Personnalisation des recommandations | Intérêt légitime | | **Centres d'intérêt** | Algorithme de recommandation | Consentement | | **Identité créateur** | Publication de contenu | Exécution du contrat | ### Droits des utilisateurs - **Accès** : consulter toutes ses données personnelles - **Rectification** : modifier ses informations - **Suppression** : supprimer son compte et toutes ses données - **Portabilité** : exporter ses données dans un format standard - **Opposition** : désactiver le profilage publicitaire ### Mesures techniques - Consentement explicite requis pour la géolocalisation - Anonymisation des données de localisation après 24h (sauf historique personnel) - Possibilité d'utiliser l'app en mode dégradé (sans géolocalisation précise) - Données hébergées dans l'UE # RoadWave - Architecture Technique > Les décisions techniques sont documentées dans [docs/adr/](#docs/adr/) ## Stack Technologique | Composant | Technologie | ADR | |-----------|-------------|-----| | **Backend** | Go + Fiber | [ADR-001](#docs/adr/001-langage-backend) | | **Architecture Backend** | Monolithe Modulaire | [ADR-012](#docs/adr/012-architecture-backend) | | **Authentification** | Zitadel | [ADR-008](#docs/adr/008-authentification) | | **Streaming** | HLS | [ADR-002](#docs/adr/002-protocole-streaming) | | **Codec** | Opus | [ADR-003](#docs/adr/003-codec-audio) | | **CDN** | Bunny CDN | [ADR-004](#docs/adr/004-cdn) | | **Base de données** | PostgreSQL + PostGIS | [ADR-005](#docs/adr/005-base-de-donnees) | | **ORM/Accès données** | sqlc | [ADR-013](#docs/adr/013-orm-acces-donnees) | | **Cache** | Redis Cluster | [ADR-005](#docs/adr/005-base-de-donnees) | | **Chiffrement** | TLS 1.3 | [ADR-006](#docs/adr/006-chiffrement) | | **Live** | WebRTC | [ADR-002](#docs/adr/002-protocole-streaming) | | **Frontend Mobile** | Flutter | [ADR-014](#docs/adr/014-frontend-mobile) | | **Tests** | Testify + Godog (Gherkin) | [ADR-015](#docs/adr/015-strategie-tests), [ADR-007](#docs/adr/007-tests-bdd) | | **Paiements** | Mangopay | [ADR-009](#docs/adr/009-solution-paiement) | | **Commandes volant** | Like automatique | [ADR-010](#docs/adr/010-commandes-volant) | | **Conformité stores** | CarPlay, Android Auto, App/Play Store | [ADR-011](#docs/adr/011-conformite-stores-carplay-android-auto) | --- ## Streaming Audio ### Protocole : HLS (HTTP Live Streaming) - Fonctionne à travers firewalls et réseaux mobiles instables - Cache CDN natif (réduction des coûts) - Bitrate adaptatif automatique (tunnels, zones rurales) - Support natif iOS/Android ### Codec : Opus Optimisé pour la voix en environnement bruyant (voiture). | Qualité | Bitrate | Usage | |---------|---------|-------| | Basse | 24 kbps | 2G/Edge | | Standard | 48 kbps | 3G | | Haute | 64 kbps | 4G/5G | Fallback AAC-LC pour appareils legacy. ### Buffering Adaptatif | Réseau | Buffer min | Buffer cible | Buffer max | |--------|------------|--------------|------------| | WiFi | 5s | 30s | 120s | | 4G/5G | 10s | 45s | 120s | | 3G | 30s | 90s | 300s | --- ## Sécurité ### Chiffrement - **TLS 1.3** sur tous les endpoints (overhead ~1-2%) - **DTLS-SRTP** pour WebRTC (radio live) - Pas de DRM initialement (ajout si licences l'exigent) ### Authentification - **Zitadel** (self-hosted) pour IAM - JWT validation locale (zitadel-go SDK) - OAuth2 PKCE pour mobile (iOS/Android) - MFA et passkeys disponibles - Rate limiting par IP et par utilisateur (Nginx + Zitadel) --- ## Base de Données ### PostgreSQL + PostGIS ```sql -- Requête géolocalisée typique SELECT id, ST_Distance(location::geography, ST_MakePoint($lon, $lat)::geography) as distance FROM contents WHERE ST_DWithin(location::geography, ST_MakePoint($lon, $lat)::geography, 50000) ORDER BY distance LIMIT 20; ``` ### Redis Geospatial (Cache) ``` GEOADD contents:geo longitude latitude content_id GEORADIUS contents:geo user_lon user_lat 50 km WITHDIST COUNT 20 ASC ``` TTL cache : 5 minutes (le contenu ne bouge pas). --- ## Architecture Services ``` ┌─────────────────┐ │ Bunny CDN │ Cache HLS, distribution globale └────────┬────────┘ │ ┌────────┴────────┐ │ Nginx │ SSL, rate limiting, reverse proxy └────────┬────────┘ │ ┌────────┴────────┐ │ API Gateway │ Go + Fiber └────────┬────────┘ │ ┌────┴────┬─────────────┐ │ │ │ ┌───▼───┐ ┌───▼───┐ ┌───────▼───────┐ │ Auth │ │ User │ │ Content/Geo │ │Service│ │Service│ │ Service │ └───────┘ └───────┘ └───────────────┘ │ │ │ └─────────┴─────────────┘ │ ┌─────────┴─────────┐ │ │ ┌───▼───┐ ┌─────▼─────┐ │ Redis │ │ PostgreSQL│ │Cluster│ │ + PostGIS │ └───────┘ └───────────┘ ``` --- ## Scaling 10M Utilisateurs ### Stratégie par phase | Phase | Utilisateurs | Infra | Coût estimé | |-------|--------------|-------|-------------| | MVP | 0-100K | Monolithe Go, PostgreSQL managé + Zitadel, Bunny CDN/Storage | 50-150€/mois | | Growth | 100K-1M | Kubernetes managé, replicas multi-région | 2-5K€/mois | | Scale | 1M-10M | Multi-région, Nginx origin shield, Bunny CDN | 20-50K€/mois | ### Métriques cibles | Métrique | Objectif | |----------|----------| | Latence API p99 | < 100ms | | Temps de démarrage audio | < 3s | | Disponibilité | 99.9% | | Connexions/serveur | 100K+ | --- ## Points de vigilance 1. **Buffering mobile** : Pré-chargement agressif avant tunnels (détection GPS) 2. **Handoff réseau** : Buffer suffisant pour survivre aux changements de cellule 3. **Mode offline** : Téléchargement complet sur WiFi 4. **Bande passante** : 48 kbps Opus = ~20 MB/heure (faible consommation data) --- ## Pourquoi pas UDP brut ? | UDP | HLS/TCP | |-----|---------| | Latence minimale | Latence acceptable (5-30s) | | Problèmes NAT/firewall | Passe partout | | Perte de paquets = artefacts | Retransmission automatique | | Pas de cache CDN | Cache CDN = économies | | Complexité++ | Standard de l'industrie | Pour du contenu non-interactif (podcasts, audio-guides), la latence HLS est acceptable. WebRTC réservé à la radio live uniquement. # ADR-001 : Langage Backend **Statut** : Accepté **Date** : 2025-01-17 ## Contexte RoadWave doit gérer 10M d'utilisateurs avec des connexions concurrentes massives pour le streaming audio géolocalisé. ## Décision **Go** avec le framework **Fiber**. ## Alternatives considérées | Option | Performance | Simplicité | Écosystème | |--------|-------------|------------|------------| | **Go + Fiber** | 1M+ conn/serveur | Élevée | Excellent cloud-native | | Rust + Tokio | 2M+ conn/serveur | Faible | Bon | | Node.js | 100-500K conn | Élevée | Excellent | | Elixir/Phoenix | 2M+ conn | Moyenne | Bon temps réel | ## Justification - **Performance** : Go gère 1M+ connexions par serveur avec ~10KB/connexion - **Simplicité** : Syntaxe claire, compilation rapide, facile à recruter - **Écosystème** : First-class Kubernetes, tooling natif (profiling, race detection) - **Équilibre** : Meilleur compromis performance/simplicité pour une startup ## Conséquences - Formation équipe sur Go si nécessaire - Utilisation des bibliothèques : Fiber (HTTP), pgx (PostgreSQL), go-redis # ADR-002 : Protocole de Streaming **Statut** : Accepté **Date** : 2025-01-17 ## Contexte Streaming audio vers des utilisateurs mobiles en voiture, avec réseaux instables (tunnels, zones rurales, handoff cellulaire). ## Décision **HLS** (HTTP Live Streaming) pour le contenu à la demande. **WebRTC** réservé à la radio live. ## Alternatives considérées | Option | Latence | Fiabilité mobile | Cache CDN | Complexité | |--------|---------|------------------|-----------|------------| | **HLS** | 5-30s | Excellente | Oui | Faible | | DASH | 5-30s | Bonne | Oui | Moyenne | | WebRTC | <500ms | Moyenne | Non | Élevée | | UDP brut | Minimale | Faible | Non | Très élevée | ## Justification - **Réseaux mobiles** : HLS gère les coupures et changements de cellule nativement - **Cache CDN** : Segments .ts cachables = réduction des coûts - **Compatibilité** : Support natif iOS/Android - **Bitrate adaptatif** : Ajustement automatique selon la qualité réseau ## Pourquoi pas UDP ? - Problèmes NAT/firewall sur réseaux mobiles - Perte de paquets = artefacts audio - Impossible à cacher sur CDN - Complexité sans bénéfice pour du contenu non-interactif ## Conséquences - Latence de 5-30s acceptable pour podcasts/audio-guides - WebRTC à implémenter séparément pour la radio live # ADR-003 : Codec Audio **Statut** : Accepté **Date** : 2025-01-17 ## Contexte Audio diffusé en voiture : environnement bruyant, réseau mobile variable, qualité studio non nécessaire. ## Décision **Opus** comme codec principal, **AAC-LC** en fallback. ## Profils d'encodage | Qualité | Bitrate | Usage | |---------|---------|-------| | Basse | 24 kbps | 2G/Edge | | Standard | 48 kbps | 3G | | Haute | 64 kbps | 4G/5G | ## Alternatives considérées | Codec | Bitrate | Qualité voix | Support mobile | |-------|---------|--------------|----------------| | **Opus** | 24-64 kbps | Excellente | Android natif, iOS via libs | | AAC-LC | 64-128 kbps | Bonne | Universel | | AAC-HE v2 | 32-64 kbps | Très bonne | Bon | | MP3 | 128-320 kbps | Correcte | Universel (legacy) | ## Justification - **Environnement bruyant** : Opus intègre des algorithmes de résilience au bruit - **Bande passante** : 48 kbps Opus ≈ qualité 96 kbps AAC pour la voix - **Consommation data** : ~20 MB/heure à 48 kbps - **Latence** : 2.5-60ms, idéal pour streaming adaptatif ## Conséquences - Fallback AAC-LC pour appareils legacy - Pipeline d'encodage à prévoir côté ingestion # ADR-004 : CDN **Statut** : Accepté **Date** : 2025-01-17 ## Contexte Distribution audio HLS à 10M d'utilisateurs, besoin de performance, coût maîtrisé, et indépendance vis-à-vis des géants du cloud. ## Décision **Bunny CDN** comme CDN principal. ## Alternatives considérées | Solution | Coût/mois (100TB) | Setup | Performance | Dépendance | |----------|-------------------|-------|-------------|------------| | **Bunny CDN** | ~1 000€ | 15 min | Très bon | Faible | | Cloudflare | 0-5 000€ | 5 min | Excellent | Moyenne | | CloudFront | ~9 750€ | 1h | Excellent | Forte (AWS) | | Fastly | ~12-20 000€ | 2h | Exceptionnel | Moyenne | | Nginx self-hosted | ~2-5 000€ | 1 jour | Excellent | Aucune | ## Justification - **Coût** : 10x moins cher que CloudFront - **HLS natif** : Support optimisé pour le streaming - **Simplicité** : Setup en 15 minutes, zéro maintenance - **Européen** : Conforme RGPD, 114 PoPs - **Pas de lock-in** : Migration facile si besoin ## Évolution prévue 1. **Phase 1** (0-1M users) : Bunny CDN seul 2. **Phase 2** (1-5M users) : Ajout Nginx origin shield si nécessaire 3. **Phase 3** (5M+) : Évaluation multi-CDN ## Conséquences - Configuration des règles de cache pour `.m3u8` (TTL court) et `.ts` (TTL long) - Token authentication pour protéger les segments # ADR-005 : Base de Données **Statut** : Accepté **Date** : 2025-01-17 ## Contexte Requêtes géolocalisées intensives (contenus à proximité), données utilisateurs, historiques d'écoute. ## Décision - **PostgreSQL + PostGIS** : Données persistantes et requêtes géospatiales - **Redis Cluster** : Cache géolocalisation et sessions ## Architecture ``` Requête → Redis Cache → [HIT] → Réponse ↓ [MISS] ↓ PostGIS → Cache → Réponse ``` ## Alternatives considérées | Usage | Option choisie | Alternatives | |-------|---------------|--------------| | Données utilisateurs | PostgreSQL | MySQL, MongoDB | | Géolocalisation | PostGIS | MongoDB Geo, Elasticsearch | | Cache | Redis | Memcached, KeyDB | | Analytics (futur) | ClickHouse | TimescaleDB | ## Justification ### PostgreSQL + PostGIS - Requêtes géospatiales complexes et précises - Index GIST pour performance - ACID, fiabilité éprouvée - Écosystème mature ### Redis - Cache géo natif (`GEORADIUS`) : 100K+ requêtes/sec - Sessions utilisateurs - Pub/sub pour temps réel ## Exemple de requête ```sql SELECT id, name, ST_Distance(location::geography, ST_MakePoint($lon, $lat)::geography) as distance FROM contents WHERE ST_DWithin(location::geography, ST_MakePoint($lon, $lat)::geography, 50000) ORDER BY distance LIMIT 20; ``` ## Conséquences - TTL cache Redis : 5 minutes (le contenu géolocalisé ne bouge pas) - Index GIST sur colonnes géométriques - Réplication read replicas pour scaling lecture # ADR-006 : Chiffrement **Statut** : Accepté **Date** : 2025-01-17 ## Contexte Streaming audio sur réseaux mobiles, conformité RGPD, protection du contenu. ## Décision - **TLS 1.3** sur tous les endpoints - **DTLS-SRTP** pour WebRTC (radio live) - Pas de DRM au lancement ## Alternatives considérées | Méthode | Overhead | Usage | |---------|----------|-------| | **TLS 1.3** | ~1-2% CPU | HTTPS streaming | | DTLS-SRTP | ~3-5% CPU | WebRTC temps réel | | AES-128-CBC | Minimal | Chiffrement segments HLS | | Widevine/FairPlay | Modéré | DRM (si licences l'exigent) | ## Justification ### Pourquoi chiffrer ? - **RGPD** : Protection des données utilisateurs obligatoire - **Confiance** : Standard attendu en 2025 - **Intégrité** : Empêche injection de contenu par opérateurs - **Overhead minimal** : TLS 1.3 optimisé, impact négligeable ### Pourquoi pas de DRM ? - Contenu généré par utilisateurs (pas de licences) - Complexité et coût d'intégration Widevine/FairPlay - À reconsidérer si partenariats avec labels/éditeurs ## Conséquences - Certificats SSL gérés par Bunny CDN ou Let's Encrypt - Configuration TLS 1.3 sur Nginx/API - DTLS-SRTP à implémenter pour le module radio live # ADR-007 : Tests et Spécifications Exécutables **Statut** : Accepté **Date** : 2025-01-17 ## Contexte RoadWave nécessite une documentation des use cases qui soit à la fois lisible par tous les stakeholders et vérifiable automatiquement. Les scénarios utilisateurs (touriste, routier, commerçant) doivent être validés en continu. ## Décision **Gherkin** pour les spécifications avec **Godog** comme runner de tests. ## Alternatives considérées | Option | Lisibilité | Intégration Go | Maintenance | |--------|------------|----------------|-------------| | **Gherkin + Godog** | Excellente | Native | Faible | | Gauge (Markdown) | Bonne | Plugin | Moyenne | | Tests Go natifs | Faible (devs only) | Native | Faible | | Concordion | Bonne | Java-centric | Élevée | ## Justification - **Living Documentation** : Les fichiers `.feature` servent de documentation ET de tests - **Accessibilité** : Syntaxe Given/When/Then lisible par PO, devs, testeurs - **Cohérence stack** : Godog est le standard BDD pour Go - **CI/CD** : Intégration simple dans les pipelines ## Structure ``` features/ ├── recommendation/ │ ├── geolocalisation.feature │ └── interets.feature ├── streaming/ │ ├── lecture.feature │ └── buffering.feature ├── moderation/ │ └── signalement.feature └── steps/ └── steps.go ``` ## Exemple ```gherkin Feature: Recommandation géolocalisée Scenario: Touriste près d'un monument Given un utilisateur avec l'intérêt "tourisme" à 80% And une position GPS à 100m de la Tour Eiffel When le système calcule les recommandations Then l'audio guide "Histoire de la Tour Eiffel" est en première position ``` ## Conséquences - Dépendance : `github.com/cucumber/godog` - Les use cases du README doivent être traduits en `.feature` - CI exécute `godog run` avant chaque merge # ADR-008 : Authentification et Gestion d'Identité **Statut** : Accepté **Date** : 2025-01-18 ## Contexte RoadWave nécessite un système d'authentification sécurisé pour mobile (iOS/Android), scalable jusqu'à 10M utilisateurs, avec contraintes de coût réduit et conformité RGPD. ## Décision **Zitadel** (self-hosted) pour l'IAM avec validation JWT locale côté API Go. ## Alternatives considérées | Solution | Coût (10M users) | Performance | Simplicité | Intégration Go | |----------|------------------|-------------|------------|----------------| | **Zitadel** | 200-500€/mois | Excellente | Élevée | SDK natif | | Supabase Auth | 32K€/mois | Excellente | Élevée | REST API | | Keycloak | 200-800€/mois | Bonne | Faible | Lib tierce | | Auth0 | 50K€+/mois | Excellente | Élevée | SDK natif | | JWT Custom | 0€ (dev) | Excellente | Moyenne | Natif | ## Justification - **Coût maîtrisé** : 100x moins cher que Supabase/Auth0 à 10M users - **Performance** : JWT validation locale = 0 latence auth sur chaque requête API - **Stack alignée** : Go + PostgreSQL + Redis (déjà dans RoadWave) - **Scalabilité prouvée** : Clients avec 2.3M tenants, architecture event-sourced - **RGPD natif** : Entreprise suisse, data residency EU, DPA fourni - **Standards ouverts** : OpenID Connect certifié (pas de vendor lock-in) ## Architecture ``` ┌─────────────────┐ │ Mobile Apps │ OAuth2 PKCE + Refresh tokens └────────┬────────┘ │ ┌────────▼────────┐ │ Zitadel IdP │ PostgreSQL + Redis │ (self-hosted) │ MFA, passkeys, SSO └────────┬────────┘ │ JWT token ┌────────▼────────┐ │ Go + Fiber API │ Validation JWT locale │ (RoadWave) │ github.com/zitadel/zitadel-go └─────────────────┘ ``` ## Exemple d'intégration ```go import "github.com/zitadel/zitadel-go/v3/pkg/authorization/oauth" // Validation JWT locale haute performance verifier := oauth.WithJWT(config) app.Use(verifier.Middleware()) // Accès aux claims userID := ctx.Locals("sub").(string) ``` ## Conséquences - Déploiement Docker Compose pour MVP - Migration vers Kubernetes HA en production - Gestion refresh tokens (rotation automatique) - MFA et passkeys disponibles out-of-the-box - Rate limiting intégré à Zitadel # ADR-009 : Solution de Paiement et Gestion des Abonnements **Statut** : Accepté **Date** : 2025-01-19 ## Contexte RoadWave nécessite une solution de paiement pour gérer les abonnements Premium (4.99€/mois) et reverser 70% des revenus aux créateurs de contenu. Besoin de marketplace natif (split payments), KYC automatique, conformité RGPD, et coûts maîtrisés. ## Décision **Mangopay** (France/Luxembourg) comme solution unique pour paiements, marketplace et abonnements. ## Alternatives considérées | Solution | Coût transaction | Marketplace | KYC | Souveraineté | |----------|-----------------|-------------|-----|--------------| | **Mangopay** | 1.8% + 0.18€ | ✅ Natif | ✅ Gratuit | 🇪🇺 France/LU | | Stripe Connect | 2.9% + 0.30€ | ✅ Natif | ❌ 1.20€ | 🇺🇸 USA | | Mollie | 2.9% + 0.29€ | ❌ Non | ❌ Non | 🇪🇺 Pays-Bas | | Paddle | 5% + 0.50€ | ✅ Natif | ✅ Inclus | 🇬🇧 UK | ## Justification - **38% moins cher** que Stripe (1.8% vs 2.9%) - **Marketplace natif** : E-wallets automatiques, split payments 70/30, payouts SEPA gratuits - **KYC gratuit** : vérification d'identité incluse (vs 1.20€/créateur chez Stripe) - **Souveraineté EU** : France/Luxembourg, régulé ACPR, RGPD natif - **Conformité DAC7** : reporting fiscal automatique - **Spécialisé marketplace** : utilisé par Vinted, Ulule, ManoMano ## Architecture ``` ┌────────────────────────┐ │ Utilisateurs Premium │ 4.99€/mois └───────────┬────────────┘ │ ┌───────▼───────┐ │ Mangopay │ - Abonnements récurrents │ │ - KYC créateurs (gratuit) │ │ - E-wallets automatiques └───────┬───────┘ - Payouts SEPA (gratuits) │ ┌─────────┼─────────┐ │ │ │ ┌─▼───┐ ┌─▼───┐ ┌─▼────┐ │Créa │ │Créa │ │Plate-│ │teur │ │teur │ │forme │ │ A │ │ B │ │(30%) │ │(70%)│ │(70%)│ │ │ └─────┘ └─────┘ └──────┘ ``` ## Exemple intégration ```go // Abonnement récurrent POST /v2.01/{ClientId}/recurringpayinregistrations { "AuthorId": "{UserId}", "FirstTransactionDebitedFunds": {"Currency": "EUR", "Amount": 499} } // Transfer vers créateur (70%) POST /v2.01/{ClientId}/transfers { "DebitedWalletId": "{PlatformWalletId}", "CreditedWalletId": "{CreatorWalletId}", "DebitedFunds": {"Currency": "EUR", "Amount": 349} } // Payout SEPA gratuit POST /v2.01/{ClientId}/payouts/bankwire ``` ## Conséquences - Solution tout-en-un : 1 seul prestataire vs 2-3 - Économie de 2160€/an sur 1000 abonnés (vs Stripe) - Délai activation compte : 2-5 jours - Intégration Go via REST API (pas de SDK Go officiel) - Apple/Google IAP gérés séparément (comme toute solution de paiement) # ADR-010 : Commandes au volant et likes **Statut** : Accepté **Date** : 2026-01-20 ## Contexte RoadWave est utilisée en conduisant. Les utilisateurs doivent pouvoir liker du contenu pour améliorer les recommandations, mais les commandes au volant ont des limitations : - 40% des véhicules n'ont que Suivant/Précédent/Mute - iOS/Android ne supportent pas nativement les appuis longs ou doubles-appuis - La sécurité impose des interactions minimales ## Décision **Like automatique basé sur le temps d'écoute**. Règles : - ≥80% d'écoute → Like renforcé (+2 points) - 30-79% d'écoute → Like standard (+1 point) - <30% d'écoute → Pas de like - Skip <10s → Signal négatif (-0.5 point) ## Alternatives considérées | Option | Compatibilité | Sécurité | Complexité | |--------|---------------|----------|------------| | **Like automatique** | 100% | Maximale | Faible | | Double-tap Pause | ~80% | Moyenne | Moyenne | | Appui long Suivant | ~95% | Faible | Élevée | | Configuration paramétrable | 100% | Variable | Très élevée | ## Justification - **Sécurité maximale** : Aucune action complexe en conduite - **Compatibilité universelle** : Fonctionne sur 100% des véhicules - **UX intuitive** : Comportement standard (Spotify, YouTube Music) - **Engagement** : Tous les contenus génèrent des signaux - **Simplicité** : Une seule logique à implémenter et maintenir ## Conséquences - Tracking du temps d'écoute via le player audio - Calcul du score côté backend basé sur `completion_rate` - Communication onboarding : "Vos likes sont automatiques selon votre temps d'écoute" - Possibilité de like manuel depuis l'app (à l'arrêt) - Métriques à suivre : taux de complétion, distribution des scores, feedbacks utilisateurs # ADR-011 : Conformité App Stores et Plateformes Auto **Statut** : Accepté avec actions requises **Date** : 2026-01-20 ## Contexte RoadWave est une app audio géolocalisée utilisée en conduite (CarPlay/Android Auto) avec : - Contenu généré par utilisateurs (UGC) - Monétisation : publicités géolocalisées + Premium (4.99€ web / 5.99€ IAP) - GPS en arrière-plan - Partage de revenus avec créateurs (70/30) ## Décision **Stratégie de conformité multi-plateforme** avec : - Modération UGC robuste (IA + humain) - Prix différenciés selon région (US/EU/Monde) - GPS avec disclosure complète - Paiements créateurs externes (Mangopay) ## Plateformes analysées | Plateforme | Conformité | Points critiques | |------------|------------|------------------| | **Android Auto** | ✅ Conforme | API Level 35+ (Android 15+) | | **CarPlay** | ✅ Conforme | Entitlement audio à demander | | **Google Play** | ⚠️ Actions requises | Déclaration GPS + UGC modération | | **App Store** | ⚠️ Actions requises | Prix différenciés US/EU | ## Conformité détaillée ### Android Auto / CarPlay ✅ - 100% audio (pas de vidéo) - Commandes standard au volant - Aucun achat in-car - Like automatique = sécurité maximale - **Notifications géolocalisées** : sonore uniquement en mode CarPlay/Android Auto (pas d'overlay visuel) - **Action** : Demander CarPlay Audio Entitlement (Apple) ### Google Play ⚠️ **UGC (critique)** : - Modération hybride IA + humain ✅ - 3 premiers contenus validés manuellement ✅ - Système de strikes (4 = ban) ✅ - Signalement + blocage utilisateurs ✅ **GPS Background (critique)** : - Permission "Always Location" = **OPTIONNELLE** - Demandée uniquement pour mode piéton (notifications arrière-plan audio-guides) - Justification Play Console : > "RoadWave permet aux utilisateurs de recevoir des alertes audio-guides lorsqu'ils passent à pied près de monuments/musées, même quand l'app est en arrière-plan. Cette fonctionnalité est optionnelle et peut être désactivée dans les paramètres." - In-app disclosure obligatoire (écran dédié avant demande permission) - Si refusée : app fonctionne en mode voiture uniquement - **Action** : Remplir formulaire background location Play Console avec justification **Réponses formulaire Play Console** : | Question | Réponse | |----------|---------| | Why does your app need background location? | "RoadWave offers optional pedestrian mode: users receive push notifications when passing near audio-guide points (museums, monuments) even when app is in background. This feature is opt-in and can be disabled in settings." | | Is this feature core to your app? | "No. This is an optional feature. Users can use RoadWave without background location permission (in-car mode works with foreground location only)." | | What user value does this provide? | "Pedestrian users (tourists, museum visitors) can keep phone in pocket and receive audio-guide alerts automatically without opening the app." | | Does a less invasive alternative exist? | "Yes. Users can use manual navigation (open app, select audio-guide). Background location is a convenience feature for hands-free experience." | ### App Store ⚠️ **Prix différenciés (légaux depuis 2025-2026)** : - 🇺🇸 US : Lien externe autorisé (0% commission) - 🇪🇺 EU : Paiement externe DMA (7-20% commission réduite) - 🌍 Monde : IAP obligatoire (30% commission) **UGC** : - Mode Kids obligatoire (filtrage selon âge) ✅ - Système de modération + signalement ✅ **GPS Background (critique)** : - Permission "Always Location" = **OPTIONNELLE** - Deux strings Info.plist requises : - `NSLocationWhenInUseUsageDescription` : explication mode voiture - `NSLocationAlwaysAndWhenInUseUsageDescription` : explication mode piéton (optionnel) - In-app disclosure obligatoire avant demande "Always" - Flux two-step : When In Use → Always (si user active mode piéton) - Si refusée : app fonctionne en mode voiture uniquement - **Action** : Voir strings détaillés dans [05-interactions-navigation.md](#../regles-metier/05-interactions-navigation.md#512-mode-piéton-audio-guides) ### Revenus créateurs **Position** : Paiements créateurs = "services" (comme YouTube/Uber), pas IAP - Paiement via Mangopay Connect (externe) - Commission stores uniquement sur Premium (IAP) - Comparables : YouTube AdSense, TikTok Creator Fund, Uber ## Actions bloquantes avant soumission | Action | Plateforme | Deadline | Complexité | |--------|-----------|----------|------------| | Demander CarPlay Audio Entitlement | Apple | Avant soumission iOS | Faible | | Remplir formulaire background location avec justification | Google Play | Avant soumission Android | Faible | | Implémenter disclosure GPS (écran dédié mode piéton) | iOS + Android | MVP | Moyenne | | Rendre permission "Always Location" optionnelle | iOS + Android | MVP | Moyenne | | Désactiver overlay visuel notification en CarPlay/Android Auto | iOS + Android | MVP | Moyenne | | Mettre à jour strings Info.plist avec justifications détaillées | iOS | MVP | Faible | | Finaliser système modération UGC | Google + Apple | MVP | Élevée | **Estimation totale** : +5 jours développement avant soumission stores ## Stratégie de lancement **Phase 1 - MVP** : - IAP uniquement (5.99€/mois mondial) - Modération UGC active - GPS avec disclosure - CarPlay/Android Auto basique **Phase 2 - Post-validation** : - Prix différenciés US (lien externe 4.99€) - Paiement externe EU (DMA) - Monétisation créateurs (Mangopay) ## Conséquences - Formation équipe sur politiques stores - Suivi des métriques modération (% rejet, SLA) - Migration iOS 26 SDK (Avril 2026) - API Level 35 Android (2026) - Communication transparente GPS/publicités ## Sources - [Android Auto Media Apps](https://developer.android.com/training/cars/media) - [CarPlay Developer Guide](https://developer.apple.com/carplay) - [Google Play UGC Policy](https://support.google.com/googleplay/android-developer/answer/9876937) - [App Store Guidelines](https://developer.apple.com/app-store/review/guidelines/) - [Apple DMA Update EU](https://www.revenuecat.com/blog/growth/apple-eu-dma-update-june-2025/) - [Google Background Location 2026](https://support.google.com/googleplay/android-developer/answer/9799150) # ADR-012 : Architecture Backend **Statut** : Accepté **Date** : 2025-01-20 ## Contexte RoadWave nécessite une architecture backend évolutive tout en gardant la simplicité opérationnelle pour un MVP. Le système doit supporter une croissance progressive de 0 à 10M utilisateurs. ## Décision **Monolithe modulaire** avec séparation claire en modules internes. ## Alternatives considérées | Architecture | Complexité | Coûts infra | Time to market | Évolutivité | |--------------|------------|-------------|----------------|-------------| | **Monolithe modulaire** | Faible | Faible | Rapide | 0-1M users | | Microservices | Élevée | Élevée | Lent | 1M+ users | | Hybrid (Mono + Workers) | Moyenne | Moyenne | Moyen | 100K-5M users | ## Justification - **Simplicité** : 1 seul binaire Go, déploiement trivial - **Transactions** : Communications inter-modules en mémoire (pas de latence réseau) - **Debugging** : Stack traces complètes, profiling unifié - **Coûts** : 1 serveur suffit pour 100K users (vs N services) - **Refactoring** : Modules internes bien séparés facilitent migration vers microservices si nécessaire ## Structure modulaire ``` internal/ ├── auth/ # Validation JWT, intégration Zitadel ├── user/ # Profils, centres d'intérêt ├── content/ # CRUD contenus, métadonnées ├── geo/ # Recherche géospatiale, algorithme ├── streaming/ # Génération HLS, transcoding ├── moderation/ # Signalements, workflow ├── payment/ # Intégration Mangopay └── analytics/ # Métriques écoute, jauges ``` Chaque module suit : `handler.go` → `service.go` → `repository.go`. ## Conséquences - Scaling horizontal : réplication complète du binaire (acceptable jusqu'à 1M users) - Transition vers microservices possible en phase 2 (extraction progressive des modules) - Importance de maintenir découplage fort entre modules (interfaces claires) # ADR-013 : ORM et Accès Données **Statut** : Accepté **Date** : 2025-01-20 ## Contexte RoadWave nécessite des requêtes SQL complexes (PostGIS géospatiales) avec performance optimale et type safety. Le choix entre ORM, query builder ou SQL brut impacte maintenabilité et performance. ## Décision **sqlc** pour génération de code Go type-safe depuis SQL. ## Alternatives considérées | Solution | Performance | Type Safety | Contrôle SQL | Courbe apprentissage | |----------|-------------|-------------|--------------|----------------------| | **sqlc** | Excellente | Très haute | Total | Faible | | GORM | Moyenne | Moyenne | Limité | Faible | | pgx + SQL brut | Excellente | Faible | Total | Moyenne | | sqlx | Bonne | Faible | Total | Faible | ## Justification - **Performance** : Génération compile-time, zero overhead runtime - **Type safety** : Structs Go générées automatiquement, erreurs détectées à la compilation - **Contrôle SQL** : Requêtes PostGIS complexes écrites en pur SQL (pas de limitations ORM) - **Maintenabilité** : Modifications SQL → `sqlc generate` → code mis à jour - **Simplicité** : Pas de magic, code généré lisible et debuggable ## Workflow ```sql -- queries/content.sql -- name: GetContentNearby :many SELECT id, title, ST_Distance(location, $1::geography) as distance FROM contents WHERE ST_DWithin(location, $1::geography, $2) ORDER BY distance LIMIT $3; ``` ```bash sqlc generate ``` ```go // Code Go type-safe généré automatiquement contents, err := q.GetContentNearby(ctx, location, radius, limit) ``` ## Conséquences - Dépendance : `github.com/sqlc-dev/sqlc` - Fichier `sqlc.yaml` à la racine pour configuration - Migrations gérées séparément avec `golang-migrate` - CI doit exécuter `sqlc generate` pour valider cohérence SQL/Go # ADR-014 : Frontend Mobile **Statut** : Accepté **Date** : 2025-01-20 ## Contexte RoadWave nécessite applications iOS et Android avec support CarPlay/Android Auto, lecture audio HLS avancée, géolocalisation temps réel. Le choix du framework impacte vélocité développement et performances. ## Décision **Flutter** pour iOS et Android avec codebase unique. ## Alternatives considérées | Framework | Codebase | Performance | Audio/CarPlay | Communauté | |-----------|----------|-------------|---------------|------------| | **Flutter** | Unique | Native | Excellente | Large | | React Native | Unique | Bonne | Modules natifs requis | Très large | | Native (Swift+Kotlin) | Double | Excellente | Native | Large | | Ionic/Capacitor | Unique | Moyenne | Limitée | Moyenne | ## Justification - **Codebase unique** : iOS + Android maintenus ensemble, vélocité développement x2 - **Performance** : Dart compilé en code natif (pas de bridge JS) - **Audio HLS** : Package `just_audio` mature avec support HLS, buffering adaptatif - **CarPlay/Android Auto** : Support via packages communautaires (`flutter_carplay`, `android_auto_flutter`) - **Géolocalisation** : `geolocator` robuste avec gestion permissions - **Écosystème** : Widgets riches (Material/Cupertino), state management mature (Bloc, Riverpod) ## Packages clés ```yaml dependencies: flutter_bloc: ^8.1.3 # State management just_audio: ^0.9.36 # Lecture audio HLS geolocator: ^11.0.0 # GPS temps réel (mode voiture) geofence_service: ^5.2.0 # Geofencing arrière-plan (mode piéton) flutter_local_notifications: ^17.0.0 # Notifications géolocalisées dio: ^5.4.0 # HTTP client flutter_secure_storage: ^9.0.0 # Tokens JWT cached_network_image: ^3.3.1 # Cache images ``` **Nouveaux packages (contenus géolocalisés)** : - **`geofence_service`** : Détection entrée/sortie rayon 200m en arrière-plan (mode piéton) - Geofencing natif iOS/Android - Minimise consommation batterie - Supporte notifications push même app fermée - **`flutter_local_notifications`** : Notifications locales avec compteur dynamique - Notification avec compteur décroissant (7→1) en mode voiture - Icônes personnalisées selon type contenu - Désactivation overlay en mode CarPlay/Android Auto (conformité) ## Structure application ``` lib/ ├── core/ # Config, DI, routes ├── data/ # Repositories, API clients ├── domain/ # Models, business logic ├── presentation/ # UI (screens, widgets, blocs) └── main.dart ``` ## Conséquences - Équipe doit apprendre Dart (syntaxe proche Java/TypeScript) - Taille binaire : 8-15 MB (acceptable) - Tests : `flutter_test` pour widgets, `integration_test` pour E2E - CI/CD : Fastlane pour déploiement stores # ADR-015 : Stratégie Tests **Statut** : Accepté **Date** : 2025-01-20 ## Contexte RoadWave nécessite une couverture tests robuste avec documentation vivante des use cases. La stratégie doit équilibrer vélocité développement et qualité. ## Décision Approche **multi-niveaux** : unitaires, intégration, BDD (Gherkin), E2E, load testing. ## Stratégie par type | Type | Framework | Cible | Fréquence | |------|-----------|-------|-----------| | **Unitaires** | Testify | 80%+ couverture | Chaque commit | | **Intégration DB** | Testify + Testcontainers | Repositories critiques | Avant merge PR | | **BDD (Gherkin)** | Godog | User stories | Avant release | | **E2E Mobile** | Flutter integration_test | Parcours critiques | Nightly | | **Load** | k6 | N/A | Avant mise en prod | ## Tests unitaires (Testify) ```go // internal/user/service_test.go func TestGetUserByID(t *testing.T) { mockRepo := new(MockRepository) service := NewService(mockRepo) mockRepo.On("FindByID", "123").Return(&User{ID: "123"}, nil) user, err := service.GetByID("123") assert.NoError(t, err) assert.Equal(t, "123", user.ID) mockRepo.AssertExpectations(t) } ``` **Couverture minimale** : 80% sur packages `internal/*/service.go` ## Tests BDD (Gherkin + Godog) Voir [ADR-007](#007-tests-bdd) pour contexte complet. ```gherkin # features/recommendation.feature Feature: Recommandation géolocalisée Scenario: Contenu proche prioritaire Given je suis à Paris (48.8566, 2.3522) And un contenu existe à 500m avec tag "tourisme" And mon intérêt "tourisme" est à 85% When je demande des recommandations Then le contenu est en première position And le score de pertinence est supérieur à 0.8 ``` **Couverture** : Tous les cas d'usage du [README.md](#../../README) traduits en `.feature`. ## Tests intégration (Testcontainers) ```go // internal/geo/repository_integration_test.go func TestFindContentNearby(t *testing.T) { container := testcontainers.RunPostGISContainer(t) defer container.Terminate() repo := NewRepository(container.DB()) // Insert test data repo.CreateContent(testContent) // Query results := repo.FindNearby(48.8566, 2.3522, 5000) assert.Len(t, results, 1) } ``` ## Tests E2E Mobile (Flutter) ```dart // integration_test/player_test.dart testWidgets('Play audio and skip', (tester) async { await tester.pumpWidget(MyApp()); await tester.tap(find.byIcon(Icons.play_arrow)); await tester.pumpAndSettle(); expect(find.text('Now Playing'), findsOneWidget); await tester.tap(find.byIcon(Icons.skip_next)); expect(find.text('Next Content'), findsOneWidget); }); ``` ## Load testing (k6) ```javascript // tests/load/streaming.js import http from 'k6/http'; import { check } from 'k6'; export let options = { stages: [ { duration: '2m', target: 1000 }, { duration: '5m', target: 10000 }, ], }; export default function () { let res = http.get('https://api.roadwave.com/v1/content/nearby'); check(res, { 'status is 200': (r) => r.status === 200 }); } ``` **Objectif** : API p99 < 100ms à 10K RPS. ## CI/CD Pipeline ```yaml # .github/workflows/ci.yml - name: Unit tests run: go test -race -coverprofile=coverage.out ./... - name: BDD tests run: godog run features/ - name: Integration tests run: go test -tags=integration ./... - name: Coverage gate run: | coverage=$(go tool cover -func=coverage.out | grep total | awk '{print $3}' | sed 's/%//') if (( $(echo "$coverage < 80" | bc -l) )); then echo "Coverage $coverage% < 80%" exit 1 fi ``` ## Conséquences - Dépendances : - `github.com/stretchr/testify` - `github.com/cucumber/godog` - `github.com/testcontainers/testcontainers-go` - `grafana/k6` - Temps CI : ~3-5 min (tests unitaires + BDD) - Tests intégration/E2E : nightly builds (15-30 min) - Load tests : avant chaque release majeure # Règles métier RoadWave > Documentation complète des règles métier validées pour l'application RoadWave. > Chaque section détaille les comportements, flux et décisions techniques. --- ## 📋 Table des matières ### [01. Authentification & Inscription](#01-authentification-inscription) **Contenu** : Inscription, connexion, récupération de compte - Inscription : email/password uniquement (pas d'OAuth tiers) - Vérification email : optionnelle auditeurs (limite 5 contenus), obligatoire créateurs (lien expire 7j) - Connexion : 5 tentatives max, blocage 15 min, refresh token 30j - Récupération mot de passe : email, lien expire 1h --- ### [02. Algorithme de recommandation](#02-algorithme-recommandation) **Contenu** : Scoring, géolocalisation, orientation politique, mode Kids - Classification géo : Ancré (70%) / Contextuel (50%) / Neutre (20%) - Engagement : 20%, Aléatoire : 10% - Orientation politique : 5 niveaux, équilibre imposé (40/40/20) - Mode Kids : 4 tranches (3-6 / 6-9 / 9-12 / 13-15 ans), activation auto <13 ans - Historique : >80% jamais reproposer, <10s ne pas reproposer --- ### [03. Centres d'intérêt et jauges](#03-centres-interet-jauges) **Contenu** : Évolution jauges, valeurs initiales - Like automatique : écoute ≥80% → +2%, écoute 30-79% → +1% - Like explicite (manuel) : +2% (cumulable avec auto) - Abonnement : +5% - Skip rapide (<10s) : -0.5% - Valeur initiale : 50% (neutre) - Limites : 0-100% stricte, pas de dégradation temporelle --- ### [04. Création et publication de contenu](#04-creation-publication-contenu) **Contenu** : Upload, métadonnées, validation, modification - Formats : MP3, AAC (.mp3, .aac, .m4a), max 200 MB, 4h - Métadonnées obligatoires : titre, type géo, zone, tags (1-3), classification âge - Validation 3 premiers contenus : 24-48h (modération RoadWave) - Modification : métadonnées uniquement, pas audio/zone/classification --- ### [05. Interactions et navigation](#05-interactions-navigation) **Contenu** : Commandes Suivant/Précédent, interactions volant, lecture en boucle - Suivant : pré-calcul 5 contenus, recalcul >10km ou 10 min - Précédent : <10s → contenu avant, ≥10s → replay début - Commandes volant : Suivant, Précédent, Play/Pause uniquement - Like automatique : ≥80% écoute → +2 points, 30-79% → +1 point - Actions manuelles : bouton cœur (arrêt véhicule) ou vocal (CarPlay/Android Auto) - Passage auto après 2s (1s mode Kids) --- ### [06. Publicités](#06-publicites) **Contenu** : Campagnes, fréquence, insertion, facturation - Interface self-service, budget min 50€, étalement paramétrable - Fréquence : 1/5 contenus (gratuits uniquement) - Durée : 10-60s (recommandé 15-30s), skippable après 5s - Validation manuelle 24-48h, prépaiement Mangopay - Facturation : écoute complète 0.05€, skip après 5s : 0.02€, skip immédiat : 0€ --- ### [07. Radio live](#07-radio-live) **Contenu** : Démarrage, arrêt, comportement auditeur - Buffer 15s avant diffusion publique, durée max 8h - Notification push abonnés dans zone géo uniquement - Arrêt : compte à rebours 5s (manuel) ou auto si déco ≥60s - Enregistrement auto MP3 256 kbps → replay sous 5-10 min - Auditeur : buffer 15s, continuation si sortie zone, AUCUN chat --- ### [08. Abonnements et notifications](#08-abonnements-notifications) **Contenu** : Impact algorithme, notifications, audio-guides, limites - Boost +30% au score final (pas priorité absolue) - Détection contexte : <5 km/h piéton, >10 km/h voiture - Voiture : in-app uniquement, Piéton : push actives - Limite 10 notifications push/jour (5-20), mode silencieux 22h-8h - Audio-guide piéton : détection <100m lieu, page sélection, navigation manuelle - Max 200 abonnements, +5% jauges tous tags créateur --- ### [09. Monétisation créateurs](#09-monetisation-createurs) **Contenu** : Activation, KYC, sources revenus, paiement - Conditions : compte ≥3 mois, ≥500 abonnés, ≥10K écoutes, 0 strike, ≥5 contenus/90j - KYC via Mangopay Connect : SIRET, TVA, RIB pro, pièce ID, Kbis <3 mois - Revenus pub : 3€ / 1000 écoutes complètes (6% CA pub) - Revenus Premium : 70% créateur, 30% plateforme (proportionnel temps écoute) - Paiement : seuil 50€, mensuel (15 du mois suivant), SEPA Mangopay --- ### [10. Premium](#10-premium) **Contenu** : Offre, multi-devices, avantages, gestion abonnement - Prix : 4.99€/mois OU 49.99€/an (4.16€/mois effectif) - Pas d'essai gratuit, pas de partage familial (MVP) - Multi-devices : 1 seul stream actif, détection connexion simultanée - Avantages : 0 pub, contenus exclusifs 👑, qualité 64 kbps Opus, offline illimité - Paiement : Mangopay (web) ou IAP iOS/Android 5.99€/mois (+30% commission) --- ### [11. Mode offline](#11-mode-offline) **Contenu** : Téléchargement, validité, synchronisation - Zone géographique : choix manuel (autour de moi / ville / département / région) - Nombre contenus : gratuit 50 max, Premium illimité - WiFi par défaut, mobile avec confirmation + estimation volume - Validité : 30 jours, renouvellement auto si WiFi (contenus >25 jours) - Sync : likes/abonnements batch auto à reconnexion, queue actions 7j max --- ### [12. Gestion des erreurs](#12-gestion-erreurs) **Contenu** : Aucun contenu, contenu supprimé, perte réseau, GPS désactivé - Aucun contenu : élargissement auto 50km → 100km → département → région → national - Contenu supprimé : laisser terminer, passage auto suivant après 2s - Perte réseau : buffer adaptatif (WiFi 5-120s, 4G 10-120s, 3G 30-300s), retry 5s max 6× - GPS désactivé : mode dégradé (contenu national + neutre + téléchargé) --- ### [13. Conformité RGPD](#13-conformite-rgpd) **Contenu** : Consentements, anonymisation, export, suppression - Consentement : Tarteaucitron.js + PostgreSQL versioning - GPS précis : 24h puis geohash 5 (~5km²) - Export : JSON + HTML + audio → ZIP, génération asynchrone sous 48h, expire 7j - Suppression : grace period 30j, contenus créés anonymisés (créateur = "Utilisateur supprimé") - Analytics : Matomo self-hosted, IP anonymisées, 0 cookie tiers - DPO : fondateur formé CNIL (non obligatoire <250 employés) --- ### [14. Modération - Flows opérationnels](#14-moderation-flows) **Contenu** : Signalement, traitement, sanctions - Signalement : 7 catégories (haine, sexuel, illégalité, droits auteur, spam, fake news, autre) - IA pré-filtre : Whisper large-v3 (transcription) + NLP open source (1-10 min) - SLA : Critique <2h (24/7), Haute/Moyenne <24h, Basse <72h - Notification sanction : email + push + in-app (détail complet : catégorie, timestamp, transcription) - Appel : formulaire in-app, délai 7j max, réponse 72h garanti (standard) --- ### [15. Autres comportements](#15-autres-comportements) **Contenu** : Partage, profil créateur, recherche - Partage : bouton partout, lien `roadwave.fr/share/c/[id]`, web player + deep link - Profil créateur : @pseudo, bio (300 car), stats publiques arrondies, badge vérifié ✓ - Badge vérifié : KYC validé OU célébrité OU >10K abonnés - Recherche : full-text PostgreSQL (français, stemming), recherche géo (Nominatim OSM) - Filtres : type, durée, âge, géo, tags, date, premium (combinables) - Affichage : liste enrichie (20/page, infinite scroll) + vue carte Leaflet --- ### [16. Audio-guides multi-séquences](#16-audio-guides-multi-sequences) **Contenu** : Modes déplacement, navigation, déclenchement GPS, publicités - **4 modes** : 🚶 Piéton (manuel) / 🚗 Voiture (GPS auto + manuel) / 🚴 Vélo / 🚌 Transport - **Mode Piéton** : pause auto après chaque séquence, user clique Suivant, navigation libre - **Mode Voiture** : déclenchement GPS auto (rayon 30m), boutons manuels actifs, warning sécurité >10 km/h - **Affichage voiture** : distance temps réel + ETA + direction (flèche) + vitesse - **Rayons** : Voiture 30m, Vélo 50m, Transport 100m (configurable créateur 10-200m) - **Publicités** : 1/5 séquences tous modes, auto-play, skippable 5s - **Reprise** : sauvegarde auto (séquence + position exacte), popup si <30j, multi-device (sync cloud) --- ## 🗂️ Organisation Chaque fichier de règles métier suit la structure : 1. **Décisions** : choix validés avec justifications 2. **Comportements détaillés** : flux utilisateur, cas limites 3. **Paramètres** : valeurs exactes, seuils, durées 4. **Points d'attention Gherkin** : éléments à tester --- ## 🚀 Utilisation Ces documents servent de **référence unique** pour : - ✅ Développement backend/frontend - ✅ Écriture des tests Gherkin (BDD) - ✅ Validation QA - ✅ Documentation produit **Prochaine étape** : Création des fichiers `.feature` Gherkin dans `features/` basés sur ces règles. --- ## 📊 Statistiques - **16 sections** validées - **~12 000 lignes** de spécifications détaillées - **Coût infrastructure MVP** : ~50-250€/mois (hors salaires) - **Technologies** : 100% open source (sauf Mangopay paiements) --- **Dernière mise à jour** : Janvier 2026 **Statut** : ✅ Toutes sections validées ## 1. Authentification & Inscription ### 1.1 Méthodes d'inscription **Décision** : Email/Password uniquement (pas d'OAuth tiers) - ❌ Pas de Google, Apple, Facebook OAuth (dépendance services US/Chine) - ✅ Email + mot de passe - ✅ 2FA (Two-Factor Authentication) disponible - ✅ Option "Appareil de confiance" (skip 2FA pour 30 jours) **Justification** : - Souveraineté : pas de dépendance externe - RGPD : données 100% contrôlées - Coût : 0€ (Zitadel intégré) --- ### 1.2 Vérification email **Décision** : Différenciée selon le rôle utilisateur #### Pour les auditeurs (écoute uniquement) | État | Capacités | |------|-----------| | **Email non vérifié** | Lecture illimitée + création max 5 contenus | | **Email vérifié** | Toutes fonctionnalités débloquées | **Paramètres** : - Lien de vérification expire après **7 jours** - Possibilité de renvoyer le lien (max 3 fois/jour) - Rappel in-app après création du 3ème contenu **Justification** : - Friction minimale à l'inscription - Anti-spam sans bloquer l'essai du produit - Incitation naturelle à vérifier (déblocage) #### Pour les créateurs (monétisation) **Vérification obligatoire sous 7 jours** pour : - Accès au programme de monétisation - KYC et reversement des revenus (conformité Mangopay) - Publication illimitée de contenus **Justification** : - **Conformité légale** : KYC obligatoire pour transferts financiers - **Anti-fraude** : Vérification identité réelle pour paiements - **Responsabilité** : RoadWave doit pouvoir prouver identité créateurs monétisés --- ### 1.3 Données requises à l'inscription **Obligatoires** : - ✅ Email (format validé) - ✅ Mot de passe (voir règles ci-dessous) - ✅ Pseudo (3-30 caractères, alphanumérique + underscore) - ✅ Date de naissance (vérification âge minimum) **Optionnelles** : - ❌ Nom complet (privacy by design) - ❌ Photo de profil (avatar par défaut généré) - ❌ Bio (ajout ultérieur) **Âge minimum** : - **13 ans minimum** (conformité réglementation réseaux sociaux EU) - Vérification à l'inscription via date de naissance - Blocage inscription si <13 ans avec message explicite **Justification** : - RGPD minimal data - Friction réduite (4 champs max) - Protection mineurs (obligation légale) --- ### 1.4 Tranches d'âge des contenus **Décision** : Classification obligatoire des contenus **Catégories** : - 🟢 **Tout public** (défaut) - 🟡 **13+** : contenu mature léger (débats, actualité sensible) - 🟠 **16+** : contenu mature (violence verbale, sujets sensibles) - 🔴 **18+** : contenu adulte (langage explicite, sujets réservés) **Règles de diffusion** : - Utilisateur 13-15 ans → contenus 🟢 uniquement - Utilisateur 16-17 ans → contenus 🟢 🟡 - Utilisateur 18+ → tous contenus **Modération** : - Vérification obligatoire de la classification lors de la validation - Reclassification possible par modérateurs - Strike si classification volontairement incorrecte **Justification** : - Protection mineurs (obligation légale) - Responsabilité plateforme - Coût : champ supplémentaire + règle algo --- ### 1.5 Validation mot de passe **Règles** : - ✅ Minimum **8 caractères** - ✅ Au moins **1 majuscule** - ✅ Au moins **1 chiffre** - ❌ Pas de symbole obligatoire (simplicité) **Validation** : - Côté client (feedback temps réel) - Côté backend (sécurité) - Message d'erreur explicite par règle non respectée **Justification** : - Standard industrie - Bloque 95% des mots de passe faibles - UX acceptable (pas trop restrictif) --- ### 1.6 Two-Factor Authentication (2FA) **Décision** : Optionnel mais recommandé **Méthodes disponibles** : - ✅ TOTP (Time-based One-Time Password) via app (Google Authenticator, Authy) - ✅ Email (code 6 chiffres, expire 10 min) - ❌ SMS (coût élevé ~0.05€/SMS) **Appareil de confiance** : - Option "Ne plus demander sur cet appareil" → bypass 2FA pendant **30 jours** - Révocable depuis paramètres compte - Liste des appareils de confiance visible **Justification** : - Sécurité renforcée sans coût SMS - UX : appareil de confiance évite friction quotidienne - Zitadel natif (0€) --- ### 1.7 Tentatives de connexion **Règles** : - Maximum **5 tentatives** par période de **15 minutes** - Blocage temporaire après 5 échecs - Compteur reset automatique après 15 min - Notification email si blocage (tentative suspecte) **Déblocage** : - Automatique après 15 min - Ou via lien "Mot de passe oublié" **Justification** : - Anti brute-force - Standard industrie (équilibre sécurité/UX) - Zitadel natif (0€) --- ### 1.8 Sessions et refresh tokens **Durée de vie** : - **Access token** : 15 minutes - **Refresh token** : 30 jours **Rotation** : - Refresh token rotatif (nouveau token à chaque refresh) - Ancien token invalidé immédiatement - Détection token replay attack **Extension automatique** : - Si app utilisée, session prolongée automatiquement - Inactivité 30 jours → déconnexion **Justification** : - Sécurité (token court-vie) - UX (pas de reconnexion fréquente) - Standard OAuth2/OIDC --- ### 1.9 Multi-device **Décision** : Sessions simultanées illimitées **Gestion** : - Liste des devices connectés visible (OS, navigateur, dernière connexion, IP/ville) - Révocation individuelle possible - Révocation globale "Déconnecter tous les appareils" **Alertes** : - Notification push + email si connexion depuis nouveau device - Détection localisation suspecte (IP pays différent) **Justification** : - UX maximale (écoute voiture + tablette maison + web) - Sécurité via transparence (utilisateur voit tout) - Coût : table sessions PostgreSQL --- ### 1.10 Récupération de compte **Méthode** : Email uniquement **Processus** : 1. Utilisateur clique "Mot de passe oublié" 2. Email avec lien de reset envoyé 3. Lien expire après **1 heure** 4. Page de reset : nouveau mot de passe (validation règles) 5. Confirmation + déconnexion tous devices (sauf celui en cours) **Notifications** : - Email immédiat si changement mot de passe - Push si changement depuis appareil non reconnu **Limite** : - Maximum **3 demandes/heure** (anti-spam) **Justification** : - Standard sécurité - Pas de coût SMS - Protection contre attaque sociale --- ## Récapitulatif Section 1 ## 2. Algorithme de recommandation ### 2.1 Classification de géo-pertinence **Décision** : 3 types de contenus selon leur pertinence géographique | Type | Description | Exemple | Pondération géo | |------|-------------|---------|-----------------| | **Géo-ancré** | Contenu lié à un lieu précis | Audio-guide monument, pub restaurant local | 70% | | **Géo-contextuel** | Pertinent dans une zone | Actualité régionale, événement local | 50% | | **Géo-neutre** | Universel, pas de lien géo | Podcast philosophie, musique | 20% | **Qui décide** : - ✅ Créateur choisit le type à la publication - ✅ Modération peut reclassifier après validation - ✅ Modification possible après publication (tout le monde a le droit de se tromper) **Justification** : - Différencie audio-guide (hyper-local) des podcasts génériques - Algorithme adapte automatiquement la pondération - Coût : champ supplémentaire en DB + règle algo --- ### 2.2 Formule de scoring **Décision** : Score combiné dynamique selon type de contenu ``` score_final = (score_geo * poids_geo_type) + (score_interets * poids_interets_type) + (score_engagement * 0.2) + (bonus_aleatoire) où : - score_geo = 1 - (distance_km / distance_max_km) - score_interets = moyenne des jauges utilisateur pour les tags du contenu - score_engagement = (taux_completion * 0.5) + (ratio_likes * 0.3) + (ratio_abonnements * 0.2) - bonus_aleatoire = 10% des recommandations tirées aléatoirement ``` **Pondérations par type** : | Type | Poids géo | Poids intérêts | |------|-----------|----------------| | Géo-ancré | 0.7 | 0.1 | | Géo-contextuel | 0.5 | 0.3 | | Géo-neutre | 0.2 | 0.6 | **Paramètres** : - Distance max recommandée : **200 km** - Dégradation : **linéaire** (1 - distance/200km) - Rayon point GPS : **500m** (adapté au volume de contenu local) **Tous ces paramètres sont configurables à chaud via interface admin.** **Justification** : - Flexibilité totale selon type de contenu - Linéaire = rattrapage naturel du contenu viral ancien - Auditable via métriques engagement (moyenne/médiane) --- ### 2.3 Score d'engagement et popularité **Décision** : Intégration popularité avec poids 0.2 **Métriques** : - **Taux de complétion** : écoutes >80% / total écoutes (poids 0.5) - **Ratio likes** : likes / écoutes (poids 0.3) - **Ratio abonnements** : nouveaux abonnés après écoute / écoutes (poids 0.2) **Seuil minimum** : - Minimum **50 écoutes** avant de considérer l'engagement - Contenu <50 écoutes : score engagement = 0.5 (neutre) **Contenu viral** : - Un contenu viral à Paris **peut** être proposé à Marseille - Score géo faible compensé par score engagement élevé - Paramétrable admin **Dépréciation temporelle** : - Pas de dépréciation automatique - Ratio linéaire = contenu ancien mais toujours apprécié reste pertinent **Justification** : - Équilibre découverte / qualité - Pas de pénalisation arbitraire des contenus anciens - Coût : calculs sur métriques existantes --- ### 2.4 Part d'aléatoire (exploration) **Décision** : 10% par défaut, paramétrable utilisateur **Fonctionnement** : - 1 contenu sur 10 = tirage aléatoire (hors historique déjà écouté) - Utilisateur peut ajuster : curseur 0% (aucun aléatoire) à 50% (exploration max) **Curseur utilisateur** : - 🎯 **0%** : Personnalisé max (recommandations strictes) - ⚖️ **10%** : Équilibré (défaut) - 🎲 **30%** : Découverte élevée - 🌍 **50%** : Découverte max (équivaut à national = découverte) **Justification** : - Évite la bulle de filtre - Laisse l'utilisateur maître de son expérience - Coût : variable aléatoire en algo --- ### 2.5 Contenu politique (version MVP simplifiée) > ⚠️ **Note** : La classification politique avancée (échelle gauche/droite, équilibrage imposé) a été reportée post-MVP. Voir [ANNEXE-POST-MVP.md](#ANNEXE-POST-MVP) pour la version complète. **Décision MVP** : Tag simple "Politique" sans classification idéologique **Tagging** : - Créateur peut taguer son contenu comme "Politique" (optionnel) - Tag "Politique" au même niveau que "Économie", "Sport", "Culture", etc. - **Pas de classification gauche/droite** - **Pas d'équilibrage imposé** **Filtrage utilisateur** : - Option paramètres : **"Masquer contenu politique"** - Si activé → 0% de contenus tagués "Politique" dans le feed - Par défaut : désactivé (tous contenus visibles) **Justification MVP** : - **Simplicité** : Pas de modération politique coûteuse (~2000€/mois économisés) - **Neutralité technique** : Aucun jugement éditorial sur orientation - **Risque minimal** : Évite controverses et contentieux DSA au lancement - **Fonctionnel** : Utilisateurs peuvent filtrer si souhaité **Post-MVP** : - Classification avancée possible si forte demande utilisateurs - Nécessite ressources modération dédiées et audit DSA --- ### 2.6 Mode Kids (13-15 ans) **Décision** : Mode optionnel pour adolescents 13-15 ans uniquement > ⚠️ **Note** : Âge minimum d'inscription = **13 ans** (obligation légale EU). Pas d'utilisateurs <13 ans sur la plateforme. **Tranche concernée** : | Tranche | Description | Contenus autorisés | Restrictions | |---------|-------------|-------------------|--------------| | **13-15 ans** | Collège | Contenus "Tous publics" uniquement | Filtrage 16+ et 18+ | **Activation** : - ❌ **Pas d'activation automatique** (tous les utilisateurs ont ≥13 ans) - ✅ **Activation manuelle** via toggle paramètres - ✅ Parents peuvent activer pour leurs enfants 13-15 ans - ✅ Utilisateur peut désactiver à tout moment **Filtrage quand Mode Kids activé** : - ✅ Contenus "Tous publics" uniquement - ❌ Exclusion contenus 16+ et 18+ - ❌ Pas de contenu politique (automatiquement filtré) - ❌ Pas de publicité (ou uniquement pub validée manuellement) **Interface** : - Interface standard (pas d'interface dédiée enfants pour MVP) - Filtrage algorithmique des contenus inappropriés **Justification** : - **Conformité légale** : Âge minimum 13 ans (RGPD, DSA) - **Simplicité MVP** : Un seul mode optionnel vs 4 tranches d'âge - **Protection mineurs** : Filtrage contenus adultes pour 13-15 ans - **Flexibilité** : Parents décident d'activer ou non --- ### 2.7 Déclenchement géographique **Décision** : Notification au passage, pas d'anticipation **Fonctionnement** : 1. Utilisateur passe à <500m d'un point GPS (contenu géo-ancré) 2. **Notification sonore** (bip court) + **visuelle** (logo selon type) 3. Types de logos : 📍 Info, 🏛️ Culturel, 🍴 Commercial, 🎭 Événement 4. Délai réaction utilisateur : **5 secondes** pour accepter (bouton volant ou commande vocale) 5. Si accepté → lecture immédiate 6. Si ignoré → contenu proposé normalement en file d'attente **Publicités** : - ⚠️ **Jamais d'interruption** de contenu en cours - Pub s'intercale **entre deux séquences** uniquement - Notification pub : son différent (facultatif selon paramètres) **Gestion demi-tour** : - Si utilisateur repart du point après notification → pas de nouvelle notification (déjà proposé) - Réinitialisation après 24h **Justification** : - Respect écoute en cours (pas de coupure brutale) - UX fluide (utilisateur garde contrôle) - Simplicité technique (pas de prédiction trajectoire) --- ### 2.8 Historique et repropositon **Décision** : Pas de reproposition sauf contenu partiel **Règles** : | État écoute | Completion | Action | |-------------|------------|--------| | **Écouté complètement** | >80% | ❌ Ne jamais reproposer (sauf flag `replayable = true` pour audio-guides) | | **Skippé rapidement** | <10s | ❌ Ne pas reproposer | | **Partiellement écouté** | 10-80% | ✅ Reproposer avec reprise position (`last_position_seconds`) | **Stockage historique** : - Table `user_content_history` (user_id, content_id, completion_rate, last_position, listened_at) - Historique **illimité** (PostgreSQL) - Algorithme considère les **100 derniers** pour optimisation requêtes - Export complet disponible (RGPD) **Justification** : - Découverte maximale (pas de redites) - Respect erreurs de clic (contenu partiel = 2nde chance) - Coût stockage négligeable (PostgreSQL scalable) --- ### 2.9 Paramétrabilité admin (interface dashboard) **Décision** : Tous paramètres scoring exposés + A/B testing **Paramètres configurables à chaud** : | Paramètre | Plage | Défaut | Unité | |-----------|-------|--------|-------| | `poids_geo_ancre` | 0.5 - 1.0 | 0.7 | % | | `poids_geo_contextuel` | 0.3 - 0.7 | 0.5 | % | | `poids_geo_neutre` | 0.0 - 0.4 | 0.2 | % | | `poids_engagement` | 0.0 - 0.5 | 0.2 | % | | `part_aleatoire_global` | 0.0 - 0.3 | 0.1 | % | | `distance_max_km` | 50 - 500 | 200 | km | | `rayon_gps_point_m` | 100 - 2000 | 500 | m | | `seuil_min_ecoutes_engagement` | 10 - 200 | 50 | nb | **Application changements** : - Immédiat : nouveaux calculs utilisent nouvelle config - Aucun recalcul batch (coût CPU) - Version config trackée (git-like) - Rollback 1 clic **A/B Testing** : - Création variantes (Config A vs Config B) - Split utilisateurs 50/50 aléatoire - Métriques comparatives : taux complétion, engagement, session duration - Dashboard graphique temps réel **Audit engagement** : - Métriques clés : moyenne/médiane temps d'écoute par session - Graphiques : évolution engagement selon config - Export CSV pour analyse externe **Justification** : - Optimisation continue sans redéploiement - Data-driven decisions (métriques objectives) - Coût : dashboard admin à développer (one-time) --- ### 2.10 Paramétrabilité utilisateur **Décision** : Curseurs avancés avec profils sauvegardables **Niveaux de personnalisation** : **Curseurs disponibles** : - 📍 **Géolocalisation** : Local ← slider → National (découverte = national) - 🎲 **Découverte** : 0% ← slider → 50% (part aléatoire) - ⚖️ **Politique** : Masquer / Équilibré / Mes préférences **Profils sauvegardables** : - 🚗 Trajet quotidien (boulot) : géo local, découverte 5%, politique masqué - 🛣️ Road trip : géo régional, découverte 30%, politique équilibré - 👶 Enfants : Mode Kids activé **Synchronisation** : - ✅ Sync profils entre devices (cloud PostgreSQL) - ❌ Pas de partage profils entre utilisateurs (famille) - Auto-switch selon context (détection trajet récurrent via GPS) **Sécurité conduite** : - ⚠️ **Blocage modification si vitesse GPS >10 km/h** - Warning au lancement app : "Configurez avant de prendre la route" - Modifications uniquement app arrêtée/passager **Justification** : - Utilisateur maître de son expérience - Contextes d'usage différents (quotidien vs voyage) - Sécurité routière (pas de distraction) --- ### 2.11 Médias traditionnels **Décision** : Ouverture aux médias établis **Médias autorisés** : - Presse nationale : Le Monde, Le Parisien, Libération, Le Figaro, etc. - Radios : France Inter, RTL, Europe 1, etc. - Médias régionaux : Ouest-France, Sud-Ouest, etc. **Format contenus** : - Flashs info géolocalisés (actualité régionale) - Chroniques thématiques (culture, économie, sport) - Éditos et débats (classification politique appliquée) **Validation** : - Compte média vérifié (badge ✓) - Pas de validation 3 premiers contenus (confiance établie) - Modération a posteriori uniquement **Monétisation** : - Partage revenus pub standard (même conditions créateurs) - Possibilité sponsoring direct (pas via plateforme) **Justification** : - Crédibilité plateforme (contenus professionnels) - Diversité éditoriale - Attractivité grand public (noms reconnus) --- ## Récapitulatif Section 2 ## 3. Centres d'intérêt et jauges ### 3.1 Évolution des jauges **Décision** : Système simple avec valeurs fixes | Action | Impact jauge | Justification | |--------|--------------|---------------| | **Like automatique renforcé (≥80% écoute)** | +2% | Signal fort d'intérêt (écoute quasi-complète) | | **Like automatique standard (30-79% écoute)** | +1% | Signal modéré d'intérêt | | **Like explicite (manuel)** | +2% | Signal fort, cumulable avec auto | | **Abonnement créateur** | +5% sur tous ses tags | Signal très fort d'affinité | | **Skip rapide (<10s)** | -0.5% | Désintérêt marqué | | **Skip tardif (≥30%)** | 0% | Neutre (contenu essayé suffisamment) | **Paramètres techniques** : - Les jauges sont bornées strictement entre **0% et 100%** - Calcul immédiat à chaque action (pas de batch différé) - Les tags du contenu sont définis par le créateur à la publication - Si un contenu a plusieurs tags, chaque jauge correspondante est impactée **Exemple de calcul** : ``` Contenu de 5 minutes tagué "Automobile" + "Voyage" Scénario 1 : Écoute 4min30 (90%) → Like automatique renforcé (+2%) → Jauge Automobile : 45% → 47% → Jauge Voyage : 60% → 62% Scénario 2 : Écoute 2min30 (50%) → Like automatique standard (+1%) → Jauge Automobile : 45% → 46% → Jauge Voyage : 60% → 61% Scénario 3 : Écoute 2min30 (50%) + Like manuel → Like auto +1% puis like manuel +2% = +3% total → Jauge Automobile : 45% → 48% → Jauge Voyage : 60% → 63% Scénario 4 : Skip après 5s → Signal négatif (-0.5%) → Jauge Automobile : 45% → 44.5% → Jauge Voyage : 60% → 59.5% ``` **Justification** : - **Like automatique** : Reflète l'engagement réel (voir [ADR-010](#../adr/010-commandes-volant)) - **Sécurité routière** : Pas d'action complexe en conduite - **Prévisibilité** : Règles claires et déterministes - **Coût minimal** : Calculs simples en backend - **Fiabilité** : Pas d'edge cases complexes - **Ajustable** : Valeurs modifiables via dashboard admin si besoin --- ### 3.2 Jauge initiale **Décision** : Démarrage neutre à 50%, pas de questionnaire **À l'inscription** : - Toutes les jauges d'intérêt sont initialisées à **50%** - Pas de questionnaire onboarding (friction zéro) - L'algorithme apprend naturellement via les premières écoutes **Catégories disponibles** : - Automobile - Voyage - Famille - Amour - Musique - Économie - Cryptomonnaie - Politique - Culture générale - Sport - Technologie - Santé - *... (extensible)* **Cold start (premiers jours)** : 1. Nouvel utilisateur s'inscrit → toutes jauges à 50% 2. Écoute premier podcast "Automobile" → jauge Auto monte à 51% 3. Skip un contenu "Économie" → jauge Éco descend à 48% 4. Après 10-15 écoutes, profil commence à se dessiner clairement **Alternative optionnelle (post-MVP)** : - Questionnaire **optionnel** proposé après 3 écoutes (in-app) - Message : "Améliorez vos recommandations en sélectionnant vos centres d'intérêt" - Si rempli : jauges sélectionnées passent à 70%, non sélectionnées à 30% - Si skip : conserve 50% partout **Justification** : - **Inscription ultra-rapide** : pas de questionnaire = moins de churn - **Découverte naturelle** : l'algorithme apprend en quelques écoutes - **Équitable** : pas de biais initial vers certains créateurs - **Comportement déterministe** : facile à tester et débugger - **Cold start acceptable** : à 50%, tous les contenus ont une chance égale initialement --- ### 3.3 Dégradation temporelle **Décision** : Pas de dégradation automatique Les jauges **ne diminuent jamais** avec le temps de manière automatique. **Règle** : - Une jauge ne change **que par les actions utilisateur** (like, écoute, skip) - Pas de cron job de dégradation périodique - Pas de "rafraîchissement" artificiel **Scénario illustratif** : ``` Utilisateur aimait "Économie" (jauge 80%) il y a 1 an → Depuis, skip tous les contenus Éco → Jauge descend naturellement à 40% via les skips → Pas besoin de dégradation temporelle ``` **Si utilisateur inactif longtemps** : - Utilisateur part en vacances 6 mois → jauges conservées - Au retour : ses jauges reflètent toujours ses goûts d'avant - Comportement cohérent et prévisible **Alternative utilisateur (contrôle explicite)** : - Bouton "Réinitialiser mes centres d'intérêt" dans paramètres - Action manuelle : remet toutes les jauges à 50% - Permet nouveau départ si souhaité (changement de vie, etc.) **Justification** : - **Principe KISS** (Keep It Simple, Stupid) - **Coût 0** : pas de batch nocturne, pas de calculs temporels - **Fiabilité maximale** : pas de bugs de fuseaux horaires, dates, etc. - **UX prévisible** : jauge = reflet des actions, pas d'automatisme caché - **Respect historique** : si utilisateur aimait X depuis 2 ans, pourquoi "oublier" ? - **Évolution naturelle** : les actions récentes suffisent à faire évoluer les jauges --- ## Récapitulatif Section 3 ## 4. Création et publication de contenu ### 4.1 Upload et encodage **Décision** : Formats universels avec encodage asynchrone **Formats acceptés** : - ✅ MP3 (`.mp3`) - ✅ AAC (`.aac`, `.m4a`) - ❌ WAV, FLAC (trop lourds, inutiles en voiture) **Limites** : | Paramètre | Valeur | Justification | |-----------|--------|---------------| | **Taille maximale** | 200 MB | ~4h de podcast à 128 kbps | | **Durée maximale** | 4 heures | Suffisant pour podcasts longs | | **Validation format** | Client + backend | Double sécurité | **Pipeline d'encodage** : ``` 1. Upload fichier (MP3/AAC) → Bunny Storage temporaire 2. Job asynchrone (worker Go + FFmpeg) : - Validation format et intégrité - Réencodage Opus 3 profils (24/48/64 kbps) - Génération segments HLS (.m3u8 + .ts) - Génération image couverture par défaut 3. Suppression fichier original (économie stockage) 4. Notification créateur : "Contenu prêt à publier" ``` **Temps d'encodage estimé** : - Contenu 5 min → ~30 secondes - Podcast 1h → ~5 minutes - Podcast 4h → ~20 minutes **Profils Opus générés** : | Qualité | Bitrate | Usage | |---------|---------|-------| | Basse | 24 kbps | 2G/Edge | | Standard | 48 kbps | 3G (défaut) | | Haute | 64 kbps | 4G/5G | **Écoute accélérée** : | Vitesse | Usage | |---------|-------| | 0.75x | Compréhension difficile (accent, technique) | | 1.0x | Normal (défaut) | | 1.25x | Gain léger | | 1.5x | Podcasts longs | | 2.0x | Survol rapide (modérateurs) | **Disponible pour** : - ✅ Modérateurs (validation rapide : 30s → 15s à 2x) - ✅ Auditeurs (tous les contenus) - ✅ Standard industrie (YouTube, Spotify, Apple Podcasts) **Justification** : - **Simplicité** : 2 formats couvrent 95% des cas d'usage - **Coût optimisé** : pas de conversion WAV/FLAC lourds - **Stockage réduit** : suppression original après encodage - **Scalabilité** : workers horizontalement (Kubernetes jobs) - **Productivité** : écoute accélérée = double productivité modération --- ### 4.2 Métadonnées obligatoires **Décision** : Minimaliste pour réduire friction **Champs obligatoires** : | Champ | Format | Validation | |-------|--------|------------| | **Titre** | 5-100 caractères | Alphanumérique + ponctuation basique | | **Type géo** | Enum | Ancré / Contextuel / Neutre | | **Zone diffusion** | Composite | Voir détails ci-dessous | | **Tags** | Enum | 1 à 3 parmi liste prédéfinie | | **Classification âge** | Enum | Tout public / 13+ / 16+ / 18+ | **Zone de diffusion (obligatoire)** : Options mutuellement exclusives : - **Point GPS** : latitude + longitude + rayon (100m à 10km) - **Ville** : sélection dans référentiel INSEE - **Département** : sélection liste - **Région** : sélection liste - **National** : France entière **Tags disponibles** (1 à 3 obligatoires) : - Automobile - Voyage - Famille - Amour - Musique - Économie - Cryptomonnaie - Politique - Culture générale - Sport - Technologie - Santé **Champs optionnels** : - ❌ Description (ajout ultérieur) - ❌ Image couverture (génération auto) **Image de couverture par défaut** : Génération automatique selon règles : - Icône selon type géo : 📍 Ancré / 🌍 Contextuel / 🎧 Neutre - Couleur selon tag principal : bleu (Auto), vert (Voyage), rouge (Musique), etc. - Format 800×800px, PNG - Personnalisable ultérieurement (post-MVP) **Exemple de publication** : ``` Titre : "Histoire de la Tour Eiffel" Type géo : Ancré Zone : Point GPS (48.8584, 2.2945, rayon 500m) Tags : Voyage, Culture générale Classification : Tout public → Image auto : 📍 fond bleu-vert (Voyage) ``` **Justification** : - **Friction minimale** : 5 champs max = 2 min de publication - **Publication rapide** : pas de blocage sur description/image - **Coût 0** : pas de génération IA au MVP - **Évolutif** : champs optionnels ajoutables ultérieurement --- ### 4.3 Validation des 3 premiers contenus **Décision** : Validation manuelle par équipe modération RoadWave **Processus nouveau créateur** : 1. Créateur upload ses 3 premiers contenus 2. Contenus passent en **file d'attente modération** 3. Modérateur junior RoadWave : - Écoute 30 secondes (ou 15s à 2x) - Vérifie métadonnées - Valide ou rejette avec raison 4. Si accepté : contenu publié + notification créateur 5. Si refusé : notification avec raison détaillée + lien vers règles 6. Après 3 contenus validés : créateur passe en **statut vérifié** **Critères de validation** : | Critère | Détails | |---------|---------| | **Qualité audio** | Compréhensible (pas de grésillement excessif) | | **Respect règles** | Pas de contenu prohibé évident (haine, spam, illégal) | | **Classification âge** | Cohérente avec contenu écouté | | **Tags pertinents** | Correspondance minimale avec contenu | | **Zone diffusion** | Cohérente (pas "Tour Eiffel" avec zone "National") | **Délai de validation** : - Objectif : **24-48h** (jours ouvrés) - Priorité : FIFO (First In First Out) - Weekend : délai peut atteindre 72h - Message au créateur : "Validation en cours, délai estimé 24-48h" **Notification créateur** : **Si accepté** : - Email + push : "✅ Votre contenu '[Titre]' est en ligne !" - Lien direct vers le contenu - Compteur : "2/3 contenus validés pour devenir créateur vérifié" **Si refusé** : - Email + push : "❌ Contenu '[Titre]' refusé" - Raison détaillée : "Qualité audio insuffisante" / "Tags non pertinents" / "Classification incorrecte" / etc. - Lien vers règles de publication - Possibilité de correction + resoumission **Après 3 validations** : Créateur obtient **statut "Vérifié"** : - Badge ✓ visible sur profil - Contenus futurs publiés **immédiatement** (modération a posteriori uniquement) - Modération seulement si signalé par utilisateurs **Outils modérateur** : - Écoute accélérée (1.5x ou 2x) = double productivité - Interface dédiée : queue de contenus à valider - Raccourcis clavier : A (Accepter), R (Rejeter), Espace (Pause) - Historique créateur visible (si déjà 1-2 contenus validés) **Modération communautaire (post-MVP)** : ⚠️ **Non implémenté au MVP** (complexité juridique) Vision future (envisageable) : - Créateurs établis peuvent opt-in "Modérateur communautaire" - Formation obligatoire (30 min) + quiz (80%) - Pré-validation uniquement (validation finale toujours par équipe RoadWave) - Compensation : badges, premium offert - Attribution aléatoire (pas de collusion) **Justification décision MVP** : - **Responsabilité juridique** : plateforme reste responsable (DSA EU) - **Qualité garantie** : modérateurs formés et mandatés - **Anti-spam efficace** : bloque 95% des abus dès le début - **Coût raisonnable** : 30s × 3 contenus = 1.5 min/créateur - **UX acceptable** : délai 24-48h expliqué clairement - **Pas de validation par pairs** au MVP = évite risques juridiques (collusion, compétence, conflits) --- ### 4.4 Modification et suppression **Décision** : Modification métadonnées uniquement, suppression immédiate **Modification autorisée** : | Élément | Modifiable | Justification | |---------|------------|---------------| | **Titre** | ✅ | Correction coquilles | | **Description** | ✅ | Si ajoutée ultérieurement | | **Tags** | ✅ | Ajustement pertinence | | **Image couverture** | ✅ | Personnalisation | | **Audio** | ❌ | Intégrité contenu | | **Zone diffusion** | ❌ | Évite manipulation algo | | **Type géo** | ❌ | Évite manipulation algo | | **Classification âge** | ❌ | Sécurité mineurs | **Raisons restrictions** : **Audio non modifiable** : - Évite fraude : uploader contenu validé → remplacer par spam - Intégrité : auditeurs doivent écouter ce qui a été validé **Zone/Type non modifiables** : - Évite manipulation : créer "Local Paris" → changer en "National" pour boost visibilité - Évite abus : créer "Neutre" (faible pondération géo) → changer en "Ancré" (forte pondération) **Classification non modifiable** : - Évite contournement : uploader "Tout public" → passer en "18+" sans revalidation - Sécurité : garantit que classification a été vérifiée **Si besoin de changer audio/zone/classification** : - Action : **Supprimer contenu + republier** - Si créateur <3 contenus validés : retourne en file validation - Si créateur ≥3 contenus validés : publication immédiate **Suppression de contenu** : | Aspect | Comportement | |--------|--------------| | **Délai** | Immédiat | Suppression BDD + CDN sous 5 min | | **Réversibilité** | Non | Suppression définitive | | **Historique auditeurs** | Marqué "Contenu supprimé par créateur" | Conserve écoute dans historique | | **Analytics plateforme** | Anonymisé et conservé | Métriques globales (RGPD compliant) | | **Fichiers CDN** | Supprimés sous 24h | Purge cache Bunny CDN | **Exemple scénario suppression** : ``` Créateur supprime podcast écouté par 1000 personnes → CDN : fichiers purgés sous 24h → BDD : entrée marquée "deleted", auteur anonymisé → Historique auditeurs : "Contenu supprimé" (conserve durée écoute pour stats) → Analytics : métriques globales conservées (anonymes, RGPD OK) ``` **Notifications suppression** : - Pas de notification aux auditeurs (pour éviter effet Streisand) - Historique reste consultable : "Vous avez écouté ce contenu le [date]" - Si auditeur tente de réécouter : "Ce contenu n'est plus disponible" **Justification** : - **Simplicité** : règles claires et non-ambiguës - **Sécurité** : évite manipulations algorithme et contournements modération - **Contrôle créateur** : liberté totale de supprimer (RGPD) - **Traçabilité** : historique conservé pour analytics (anonymisé) - **Coût 0** : pas de revalidation métadonnées --- ## Récapitulatif Section 4 ## 5. Interactions et navigation ### 5.1 File d'attente et commande "Suivant" **Décision** : Pré-calcul 5 contenus avec insertion prioritaire pour points géographiques **File d'attente** : - **5 contenus pré-calculés** en cache (Redis) - Recalcul automatique si : - Déplacement >10km - Toutes les 10 minutes (rafraîchissement contenu) - File d'attente <3 contenus restants **Insertion prioritaire géo-ancrée (mode voiture uniquement)** : **Détection** : - Calcul ETA (Estimated Time of Arrival) via API GPS native iOS/Android - Notification déclenchée **7 secondes avant** d'arriver au point GPS - Si vitesse < 5 km/h ET distance < 50m → notification immédiate - ⚠️ **App doit être ouverte** (pas de détection en arrière-plan en mode voiture) **Notification** : - **Sonore uniquement** : bip court ou son personnalisé RoadWave - **Visuelle minimale** : icône selon type de contenu (🏛️ culture, 👨👩👧 famille, 🎵 musique, etc.) - **Compteur visible** : 7...6...5...4...3...2...1 (décompte des secondes) - **Pas de texte affiché** (éviter distraction conducteur) - **Pas de bouton "Annuler"** : seul le bouton "Suivant" permet validation **Actions utilisateur** : 1. User entend notification sonore + voit icône et compteur 2. User appuie "Suivant" dans les 7 secondes → décompte 5s démarre 3. Pendant décompte : contenu actuel continue, compteur visible (5...4...3...2...1) 4. Si contenu actuel se termine pendant décompte → contenu suivant du buffer démarre 5. À la fin du décompte → contenu géolocalisé démarre (fade out/in 0.3s) **Si user n'appuie pas sur "Suivant"** : - Notification disparaît après 7 secondes - Contenu géolocalisé est perdu (pas d'insertion dans file) - Pas de nouveau contenu géolocalisé pendant **10 minutes** (éviter spam) **Limitation anti-spam** : - Maximum **6 contenus géolocalisés par heure** - Timer reset toutes les heures (rolling window) - Exception : séquences d'un même audio-guide multi-séquences (comptent comme 1) - Si quota atteint : notifications suivantes ignorées jusqu'à libération du quota **Invalidation immédiate** : - Utilisateur change ses préférences (curseurs géo/découverte/politique) - ⚠️ **Modification bloquée si vitesse GPS >10 km/h** (sécurité routière) - Live démarre d'un créateur suivi dans la zone **Implémentation** : ``` Redis cache : - Clé : user:{user_id}:queue - Structure : [content_1, content_2, ..., content_5] - Métadonnées : {last_lat, last_lon, computed_at, mode: "voiture"|"pieton"} - TTL : 15 minutes Tracking GPS temps réel (mobile) : - Vérification toutes les 1 seconde - Calcul ETA vers points géolocalisés proches (rayon 500m) - Si ETA ≤ 7s → trigger notification - Historique GPS : 30 derniers points pour calcul vitesse moyenne Quota anti-spam (Redis) : - Clé : user:{user_id}:geo_quota - Structure : sorted set avec timestamps des 6 derniers contenus - TTL : 1 heure - Vérification avant notification : ZCOUNT pour compter contenus dernière heure Cooldown après ignorance (Redis) : - Clé : user:{user_id}:geo_cooldown - TTL : 10 minutes - Set après notification ignorée ``` **Justification** : - **Expérience fluide** : pas de latence au clic "Suivant" - **Réactivité géo** : contenu local inséré immédiatement - **Coût optimisé** : recalcul uniquement si nécessaire - **Sécurité** : pas de modification en conduite --- ### 5.1.2 Mode piéton (audio-guides) **Décision** : Notifications push en arrière-plan avec rayon large **Contexte** : - Mode piéton détecté automatiquement si vitesse moyenne < 5 km/h - Cas d'usage : visites à pied, musées, monuments, quartiers historiques - User n'a pas besoin d'avoir l'app ouverte - ⚠️ **Fonctionnalité optionnelle** : requiert permission "localisation en arrière-plan" (activée par user) **Détection** : - App peut être en arrière-plan (si permission accordée) - Rayon de détection : **200 mètres** autour du point GPS - Geofencing iOS/Android pour minimiser consommation batterie - Permission demandée uniquement si user active "Notifications audio-guides piéton" dans settings **Notification push système** : Format : ``` Titre : "Audio-guide à proximité" Body : "[Nom du contenu] - [Nom créateur]" Action : Tap → ouvre app sur le contenu ``` Exemple : ``` Audio-guide à proximité Musée du Louvre : La Joconde - @paris_museum ``` **Permissions requises** : ⚠️ **Important** : Permission "Always Location" est **optionnelle** et demandée uniquement si user active le mode piéton dans settings. iOS (`Info.plist`) : ```xml"
**Alors** le contenu est