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,204 @@
# language: fr
Fonctionnalité: Neutralisation des pénalités de skip pour abonnés
En tant que système de jauges d'intérêt
Je veux neutraliser les pénalités de skip pour les abonnés d'un créateur
Afin de reconnaître l'affinité globale malgré des skips ponctuels contextuels
Contexte:
Étant donné qu'un utilisateur existe avec les jauges suivantes:
| catégorie | niveau |
| Automobile | 45% |
| Voyage | 60% |
Et qu'un créateur "CreateurA" publie des contenus tagués "Automobile"
# Skip <10s - Utilisateur NON abonné
Scénario: Skip rapide <10s par non-abonné - Pénalité -0.5%
Étant donné que l'utilisateur n'est PAS abonné à "CreateurA"
Et qu'un contenu "Podcast Auto" de "CreateurA" est tagué "Automobile"
Et que la jauge "Automobile" est à 45%
Quand l'utilisateur skip le contenu après 5 secondes
Alors la jauge "Automobile" descend de -0.5%
Et la jauge "Automobile" passe de 45% à 44.5%
Et cela indique un désintérêt marqué pour ce contenu
Scénario: Skip rapide <10s par non-abonné - Colonne is_subscribed=false
Étant donné que l'utilisateur n'est PAS abonné à "CreateurA"
Et qu'un contenu est skippé après 8 secondes
Quand l'événement est enregistré dans user_listening_history
Alors la colonne is_subscribed = false
Et la colonne completion_rate = 0.05 (8s sur 160s)
Et la colonne source = "recommendation"
Et ce skip compte dans les métriques d'engagement du contenu
# Skip <10s - Utilisateur ABONNÉ
Scénario: Skip rapide <10s par abonné - Pénalité neutre 0%
Étant donné que l'utilisateur EST abonné à "CreateurA"
Et qu'un contenu "Podcast Auto" de "CreateurA" est tagué "Automobile"
Et que la jauge "Automobile" est à 45%
Quand l'utilisateur skip le contenu après 5 secondes
Alors la jauge "Automobile" reste à 45% (pénalité 0%)
Et aucune pénalité n'est appliquée
Et cela reflète que l'abonnement indique une affinité globale
Scénario: Skip rapide <10s par abonné - Colonne is_subscribed=true
Étant donné que l'utilisateur EST abonné à "CreateurA"
Et qu'un contenu est skippé après 7 secondes
Quand l'événement est enregistré dans user_listening_history
Alors la colonne is_subscribed = true
Et la colonne completion_rate = 0.04 (7s sur 180s)
Et la colonne source = "recommendation"
Et ce skip NE compte PAS dans les métriques d'engagement du contenu
# Calcul métriques engagement créateur
Scénario: Métriques engagement - Skip d'abonné ne pénalise pas
Étant donné qu'un contenu "Podcast A" de "CreateurA" a reçu:
| utilisateur | abonné ? | action | source | completion_rate |
| User1 | Non | Skip <10s | recommendation | 0.05 |
| User2 | Oui | Skip <10s | recommendation | 0.04 |
| User3 | Non | Écoute complète | recommendation | 0.90 |
| User4 | Oui | Skip <10s | recommendation | 0.03 |
| User5 | Non | Écoute partielle | recommendation | 0.60 |
Quand le système calcule les métriques d'engagement du contenu
Alors les écoutes pertinentes comptabilisées sont:
| utilisateur | comptabilisé ? | raison |
| User1 | Oui | Non-abonné, source pertinente |
| User2 | Non | Abonné, skip contextuel |
| User3 | Oui | Non-abonné, source pertinente |
| User4 | Non | Abonné, skip contextuel |
| User5 | Oui | Non-abonné, source pertinente |
Et le total écoutes pertinentes = 3 (User1, User3, User5)
Et le taux de complétion = 2 complètes (User3, User5 >80%) / 3 = 66.7%
# Sources d'écoute et neutralisation
Scénario: Skip d'abonné via "recommendation" - Ne compte pas
Étant donné que l'utilisateur EST abonné à "CreateurA"
Et qu'un contenu est recommandé via l'algorithme (source="recommendation")
Quand l'utilisateur skip après 5s
Alors l'écoute NE compte PAS dans "total écoutes" pour métriques
Et la pénalité jauge est neutralisée (0%)
Scénario: Skip d'abonné via "live_notification" - Ne compte pas
Étant donné que l'utilisateur EST abonné à "CreateurA"
Et que "CreateurA" publie un contenu en live
Et qu'une notification live est envoyée (source="live_notification")
Quand l'utilisateur skip après 6s
Alors l'écoute NE compte PAS dans "total écoutes" pour métriques
Et la pénalité jauge est neutralisée (0%)
Scénario: Skip d'abonné via "search" - Ne compte pas (indépendamment abonnement)
Étant donné que l'utilisateur EST abonné à "CreateurA"
Et qu'il trouve un contenu via la recherche (source="search")
Quand l'utilisateur skip après 4s
Alors l'écoute NE compte PAS dans "total écoutes" (source non pertinente)
Et la pénalité jauge est neutralisée (0%)
Et cela s'applique à tous users (abonnés ou non) pour source="search"
Scénario: Skip non-abonné via "direct_link" - Ne compte pas
Étant donné que l'utilisateur N'est PAS abonné à "CreateurA"
Et qu'il clique sur un lien direct partagé (source="direct_link")
Quand l'utilisateur skip après 3s
Alors l'écoute NE compte PAS dans "total écoutes" (source non pertinente)
Mais la pénalité jauge s'applique quand même (-0.5%) pour non-abonné
# Reproposition
Scénario: Skip <10s non-abonné - Pas de reproposition
Étant donné que l'utilisateur N'est PAS abonné à "CreateurA"
Et qu'un contenu est skippé après 8 secondes
Quand l'algorithme calcule les prochaines recommandations
Alors ce contenu n'est jamais reproposé à cet utilisateur
Car c'est un signal négatif clair de désintérêt
Scénario: Skip <10s abonné - Reproposition possible
Étant donné que l'utilisateur EST abonné à "CreateurA"
Et qu'un contenu est skippé après 7 secondes
Quand l'algorithme calcule les prochaines recommandations plusieurs jours plus tard
Alors ce contenu PEUT être reproposé à cet utilisateur
Car l'abonnement indique une affinité globale
Et le skip peut être contextuel ("pas maintenant", "pas ce sujet")
Scénario: Stockage is_subscribed dans user_content_history
Étant donné qu'un utilisateur EST abonné à "CreateurA" au moment de l'écoute
Quand un contenu de "CreateurA" est écouté/skippé
Alors la table user_content_history enregistre:
| colonne | valeur |
| user_id | 123 |
| content_id | 456 |
| creator_id | 789 (CreateurA) |
| is_subscribed | true |
| completion_rate| 0.05 |
| source | "recommendation" |
| listened_at | 2026-02-07 10:30:00 |
# Cohérence UX
Scénario: Abonnement = Signal affinité fort malgré skip ponctuel
Étant donné que l'utilisateur est abonné à "CreateurA" depuis 6 mois
Et qu'il a écouté 50 contenus de "CreateurA" avec 90% de complétion moyenne
Quand il skip 1 contenu après 5 secondes aujourd'hui
Alors ce skip ponctuel ne pénalise pas:
| aspect | impact |
| Jauges d'intérêt user | 0% (neutre) |
| Métriques engagement créateur | Ne compte pas dans total écoutes |
| Reproposition future | Contenu peut être reproposé |
Et cela reflète que le skip est contextuel, pas un rejet du créateur
# Anti-raid naturel
Scénario: Raid malveillant via liens directs - Inefficace
Étant donné qu'un groupe malveillant veut nuire à "CreateurA"
Et qu'ils partagent des liens directs pour inciter au skip massif
Quand 1000 personnes cliquent sur le lien et skip après 2s
Alors ces 1000 skips NE comptent PAS dans les métriques engagement
Car source="direct_link" n'est pas une source pertinente
Et "CreateurA" est protégé contre ce type de raid
Scénario: Raid malveillant via recherche - Inefficace
Étant donné qu'un groupe cherche à nuire à "CreateurA"
Et qu'ils trouvent le contenu via recherche et skip massivement
Quand 500 skips rapides arrivent via source="search"
Alors ces 500 skips NE comptent PAS dans les métriques engagement
Car source="search" n'est pas une source pertinente
Et "CreateurA" est protégé
# Cas limites
Scénario: Utilisateur s'abonne pendant l'écoute d'un contenu
Étant donné qu'un utilisateur N'est PAS abonné à "CreateurA"
Et qu'il démarre l'écoute d'un contenu de "CreateurA"
Et que is_subscribed=false est enregistré au démarrage
Quand l'utilisateur s'abonne à "CreateurA" pendant l'écoute
Et qu'il skip le contenu après 8 secondes
Alors is_subscribed=false reste enregistré (état au moment du démarrage)
Et la pénalité -0.5% s'applique (car non-abonné au démarrage)
Scénario: Utilisateur se désabonne puis écoute ancien contenu
Étant donné qu'un utilisateur ÉTAIT abonné à "CreateurA"
Et qu'il se désabonne aujourd'hui
Quand il écoute un ancien contenu de "CreateurA" demain
Et qu'il skip après 6 secondes
Alors is_subscribed=false (état au moment de l'écoute)
Et la pénalité -0.5% s'applique
Et l'écoute compte dans les métriques d'engagement
# Comparaison tableaux sources
Scénario: Table récapitulative sources et abonnements
Étant donné les règles de comptabilisation définies
Quand on résume le comportement par source et abonnement
Alors le tableau complet est:
| Source | Abonné ? | Skip <10s pénalise ? | Compte "total écoutes" ? |
| recommendation | Non | Oui (-0.5%) | Oui |
| recommendation | Oui | Non (0%) | Non |
| search | Peu imp. | Variable* | Non |
| direct_link | Peu imp. | Variable* | Non |
| profile | Peu imp. | Variable* | Non |
| history | Peu imp. | Variable* | Non |
| live_notification | Non | Oui (-0.5%) | Oui |
| live_notification | Oui | Non (0%) | Non |
| audio_guide | Peu imp. | Non | Non |
(* Variable = -0.5% si non-abonné, 0% si abonné, mais source non pertinente donc pas dans métriques)