refactor(docs): réorganiser la documentation selon principes DDD

Réorganise la documentation du projet selon les principes du Domain-Driven Design (DDD) pour améliorer la cohésion, la maintenabilité et l'alignement avec l'architecture modulaire du backend.

**Structure cible:**
```
docs/domains/
├── README.md (Context Map)
├── _shared/ (Core Domain)
├── recommendation/ (Supporting Subdomain)
├── content/ (Supporting Subdomain)
├── moderation/ (Supporting Subdomain)
├── advertising/ (Generic Subdomain)
├── premium/ (Generic Subdomain)
└── monetization/ (Generic Subdomain)
```

**Changements effectués:**

Phase 1: Création de l'arborescence des 7 bounded contexts
Phase 2: Déplacement des règles métier (01-19) vers domains/*/rules/
Phase 3: Déplacement des diagrammes d'entités vers domains/*/entities/
Phase 4: Déplacement des diagrammes flux/états/séquences vers domains/*/
Phase 5: Création des README.md pour chaque domaine
Phase 6: Déplacement des features Gherkin vers domains/*/features/
Phase 7: Création du Context Map (domains/README.md)
Phase 8: Mise à jour de mkdocs.yml pour la nouvelle navigation
Phase 9: Correction automatique des liens internes (script fix-markdown-links.sh)
Phase 10: Nettoyage de l'ancienne structure (regles-metier/, diagrammes/, features/)

**Configuration des tests:**
- Makefile: godog run docs/domains/*/features/
- scripts/generate-bdd-docs.py: features_dir → docs/domains

**Avantages:**
 Cohésion forte: toute la doc d'un domaine au même endroit
 Couplage faible: domaines indépendants, dépendances explicites
 Navigabilité améliorée: README par domaine = entrée claire
 Alignement code/docs: miroir de backend/internal/
 Onboarding facilité: exploration domaine par domaine
 Tests BDD intégrés: features au plus près des règles métier

Voir docs/REFACTOR-DDD.md pour le plan complet.
This commit is contained in:
jpgiannetti
2026-02-07 17:15:02 +01:00
parent 78422bb2c0
commit 5e5fcf4714
227 changed files with 1413 additions and 1967 deletions

View File

@@ -0,0 +1,271 @@
# language: fr
Fonctionnalité: Insertion et fréquence des publicités
En tant que système RoadWave
Je veux insérer les publicités de manière équilibrée et non intrusive
Afin de préserver l'expérience utilisateur tout en monétisant
Contexte:
Étant donné que l'API RoadWave est disponible
Et qu'un utilisateur gratuit est connecté
Scénario: Fréquence par défaut 1 pub / 5 contenus
Étant donné que la fréquence par défaut est configurée à 1/5
Et que je suis un utilisateur gratuit
Quand j'écoute 5 contenus
Alors 1 publicité est insérée après le 5ème contenu
Quand j'écoute 10 contenus
Alors 2 publicités sont insérées (après les contenus 5 et 10)
Scénario: Aucune publicité pour utilisateurs Premium
Étant donné que je suis un utilisateur Premium
Quand j'écoute 100 contenus
Alors aucune publicité n'est insérée
Et je bénéficie d'une expérience sans interruption publicitaire
Scénario: Fréquence paramétrable par admin (1/3)
Étant donné que l'admin configure la fréquence à 1/3
Et que je suis un utilisateur gratuit
Quand j'écoute 6 contenus
Alors 2 publicités sont insérées (après contenus 3 et 6)
Scénario: Fréquence paramétrable par admin (1/10)
Étant donné que l'admin configure la fréquence à 1/10
Et que je suis un utilisateur gratuit
Quand j'écoute 20 contenus
Alors 2 publicités sont insérées (après contenus 10 et 20)
Scénario: Jamais d'interruption d'un contenu en cours
Étant donné que j'écoute un contenu de 10 minutes
Et que je suis à 5 minutes de lecture
Et qu'une publicité devrait être insérée selon la fréquence
Quand le système vérifie l'insertion
Alors la publicité attend la fin du contenu actuel
Et elle s'insère pendant le délai de transition (2s)
Et le contenu n'est jamais interrompu
Scénario: Insertion entre deux contenus uniquement
Étant donné que le contenu "A" se termine
Et que le délai de transition de 2s démarre
Quand le système détecte qu'une publicité doit être insérée
Alors le message "Publicité (30s)" s'affiche
Et la publicité démarre après les 2 secondes
Et l'enchaînement est naturel et fluide
Scénario: Rotation limite 3 fois/jour par utilisateur
Étant donné qu'un utilisateur a entendu la publicité "A" 3 fois aujourd'hui
Quand le système sélectionne une nouvelle publicité à diffuser
Alors la publicité "A" n'est plus éligible pour cet utilisateur aujourd'hui
Et une autre publicité "B" est sélectionnée
Et cela évite la saturation publicitaire
Scénario: Compteur de diffusions par pub et par utilisateur
Étant donné qu'un utilisateur écoute la pub "RestaurantX"
Quand la diffusion se termine
Alors un compteur Redis "pub:RestaurantX:user:123:count" s'incrémente
Et le TTL est de 24h (reset à minuit)
Quand le compteur atteint 3
Alors la pub "RestaurantX" est exclue des prochaines sélections aujourd'hui
Scénario: Limite max 6 pubs/heure par utilisateur
Étant donné qu'un utilisateur a entendu 6 publicités dans la dernière heure
Quand le système devrait insérer une 7ème pub
Alors l'insertion est reportée à l'heure suivante
Et un compteur horaire Redis "pub:user:123:hourly" est vérifié
Et cela évite le spam publicitaire
Scénario: Ciblage géographique prioritaire - Point GPS
Étant donné qu'une publicité cible un point GPS à 2km de ma position
Et qu'une autre publicité cible ma ville entière
Quand le système sélectionne une publicité
Alors la publicité point GPS est priorisée (score géo plus élevé)
Et le ciblage précis est favorisé
Scénario: Ciblage géographique prioritaire - Hiérarchie
Étant donné que 4 publicités sont éligibles:
| Publicité | Zone | Distance |
| A | Point GPS | 1km |
| B | Ville | 0km |
| C | Département | 0km |
| D | National | N/A |
Quand le système sélectionne selon priorité géographique
Alors l'ordre de priorité est: A > B > C > D
Et la publicité A (Point GPS, la plus précise) est diffusée
Scénario: Ciblage centres d'intérêt secondaire
Étant donné que 2 publicités ciblent ma zone géographique:
| Publicité | Tags | Mes jauges |
| A | Automobile | 80% |
| B | Voyage | 40% |
Quand le système applique le score centres d'intérêt
Alors la publicité A est favorisée (meilleur match jauges)
Et le ciblage thématique affine la sélection
Scénario: Ciblage horaire strict
Étant donné qu'une campagne cible uniquement 7h-9h
Et qu'il est 10h30
Quand le système sélectionne une publicité
Alors cette campagne n'est PAS éligible
Et seules les campagnes "toute la journée" ou avec plage horaire actuelle sont considérées
Scénario: Ciblage horaire pendant plage active
Étant donné qu'une campagne cible 7h-9h et 17h-19h
Et qu'il est 8h15
Quand le système sélectionne une publicité
Alors cette campagne est éligible
Et elle peut être diffusée
Scénario: Normalisation volume audio -14 LUFS
Étant donné qu'une publicité est uploadée avec volume trop élevé (-6 LUFS)
Quand le système encode l'audio via FFmpeg
Alors le volume est normalisé automatiquement à -14 LUFS
Et le publicitaire reçoit une notification "Volume audio ajusté pour conformité"
Et cela évite l'effet "pub trop forte" frustrant
Scénario: Validation volume audio lors encodage
Étant donné qu'une publicité est soumise
Quand FFmpeg encode le fichier
Alors une commande loudnorm est appliquée:
```
ffmpeg -i input.mp3 -filter:a loudnorm=I=-14:LRA=11:TP=-1 output.mp3
```
Et le fichier final respecte le standard broadcast -14 LUFS
Scénario: Sélection aléatoire si critères équivalents
Étant donné que 3 publicités ont le même score géo
Et qu'elles ont toutes des jauges centres d'intérêt équivalentes
Et qu'aucune n'a été diffusée 3 fois aujourd'hui
Quand le système sélectionne une publicité
Alors une sélection aléatoire équitable est faite
Et chaque campagne a 33% de chances d'être diffusée
Scénario: Exclusion publicités avec budget épuisé
Étant donné qu'une campagne "A" a épuisé son budget
Et qu'une campagne "B" a encore du budget disponible
Quand le système sélectionne une publicité
Alors seule la campagne "B" est éligible
Et la campagne "A" est automatiquement exclue
Scénario: Exclusion publicités hors dates de campagne
Étant donné qu'une campagne "A" est programmée du 01/02 au 14/02
Et que nous sommes le 20/01
Quand le système sélectionne une publicité
Alors la campagne "A" n'est pas éligible
Et seules les campagnes actives aujourd'hui sont considérées
Scénario: Publicité visible uniquement dans zone géographique
Étant donné qu'une publicité cible "Marseille uniquement"
Et que je suis à Lyon
Quand le système sélectionne une publicité
Alors cette publicité n'est jamais éligible pour moi
Et je ne la verrai jamais tant que je reste à Lyon
Scénario: Tracking compteur horaire avec TTL
Étant donné qu'un utilisateur entend une pub à 10h05
Quand le compteur horaire est incrémenté
Alors la clé Redis "pub:user:123:hourly:2026012110" est créée
Et le TTL est de 1 heure (expire à 11h05)
Et le système compte les pubs dans la fenêtre glissante d'1h
Scénario: Reset compteur quotidien à minuit
Étant donné qu'un utilisateur a entendu la pub "A" 3 fois le 20/01
Quand minuit passe et on est le 21/01
Alors le compteur "pub:A:user:123:count" est expiré (TTL 24h)
Et l'utilisateur peut à nouveau entendre la pub "A" jusqu'à 3 fois
Scénario: Aucune pub si aucune campagne éligible
Étant donné qu'aucune campagne n'a de budget disponible
Ou que toutes les campagnes ont déjà été diffusées 3 fois aujourd'hui
Quand le système devrait insérer une publicité
Alors aucune pub n'est insérée
Et l'enchaînement de contenus continue normalement
Et le prochain contenu démarre directement
Scénario: Priorisation campagnes avec budget important restant
Étant donné que 2 campagnes sont éligibles:
| Campagne | Budget restant | Jours restants |
| A | 500 | 2j |
| B | 50 | 10j |
Quand le système applique la priorisation budgétaire
Alors la campagne A est légèrement favorisée (urgence dépense)
Et cela aide à épuiser les budgets avant fin de campagne
Scénario: Log des sélections pour analytics
Étant donné qu'une publicité "RestaurantX" est sélectionnée
Quand elle est diffusée à l'utilisateur "123"
Alors un événement est loggé en base:
| Champ | Valeur |
| pub_id | RestaurantX |
| user_id | 123 |
| timestamp | 2026-01-21 10:30 |
| zone_geo | Marseille |
| score_geo | 0.85 |
| score_interet | 0.70 |
Et cela permet l'analytics publicitaire
Scénario: Détection changement statut utilisateur (gratuit → premium)
Étant donné que je suis un utilisateur gratuit
Et que j'entends des publicités
Quand je souscris à Premium
Alors le système détecte le changement de statut immédiatement
Et plus aucune publicité n'est insérée dès le prochain contenu
Et mon expérience devient sans pub instantanément
Scénario: Interface admin pour ajuster fréquence globale
Étant donné que je suis admin RoadWave
Quand j'accède aux paramètres publicitaires
Alors je peux ajuster le curseur de fréquence:
| Option | Fréquence |
| 1/3 | Haute (agressif) |
| 1/5 | Standard (défaut)|
| 1/7 | Modérée |
| 1/10 | Faible |
Et le changement s'applique en temps réel à tous les utilisateurs
Scénario: A/B testing fréquence sur cohortes utilisateurs
Étant donné que l'admin active un test A/B
Quand 50% des utilisateurs ont fréquence 1/5
Et 50% des utilisateurs ont fréquence 1/7
Alors les métriques sont trackées séparément:
| Cohorte | Fréquence | Taux désabonnement | Revenus/user |
| A | 1/5 | 2.5% | 0.50 |
| B | 1/7 | 1.8% | 0.40 |
Et l'admin peut identifier la fréquence optimale
Plan du Scénario: Insertion publicité selon fréquence
Étant donné que la fréquence est <frequence>
Quand j'écoute <contenus> contenus
Alors <pubs> publicités sont insérées
Exemples:
| frequence | contenus | pubs |
| 1/3 | 9 | 3 |
| 1/5 | 10 | 2 |
| 1/5 | 25 | 5 |
| 1/7 | 14 | 2 |
| 1/10 | 30 | 3 |
Plan du Scénario: Priorité géographique selon type zone
Étant donné qu'une publicité cible <type_zone>
Quand le système calcule le score géographique
Alors la priorité est <score>
Exemples:
| type_zone | score |
| Point GPS | 1.0 |
| Ville | 0.8 |
| Département | 0.6 |
| Région | 0.4 |
| National | 0.2 |
Plan du Scénario: Exclusion publicité selon compteur quotidien
Étant donné qu'une publicité a été entendue <fois> fois aujourd'hui
Quand le système vérifie l'éligibilité
Alors la publicité est <eligible>
Exemples:
| fois | eligible |
| 0 | éligible |
| 1 | éligible |
| 2 | éligible |
| 3 | non éligible |
| 4 | non éligible |