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,402 @@
# language: fr
Fonctionnalité: API - Création et gestion d'audio-guides multi-séquences
En tant que système backend
Je veux exposer des endpoints pour créer et gérer les audio-guides
Afin de permettre aux créateurs de publier des expériences guidées
Contexte:
Étant donné que l'API RoadWave est démarrée
Et que le créateur "guide@example.com" est authentifié avec un token JWT valide
Et que son compte est vérifié (email_verified: true)
# 16.1.2 - Endpoints de création
Scénario: POST /api/v1/audio-guides - Création d'un audio-guide
Étant donné la requête suivante:
"""json
{
"title": "Safari du Paugre",
"description": "Découvrez les animaux du parc en voiture sur un circuit de 5km",
"mode": "voiture",
"vitesse_recommandee": "30-50 km/h",
"tags": ["nature", "animaux", "famille"],
"classification_age": "tout_public",
"zone_diffusion": {
"type": "polygon",
"coordinates": [[2.5678, 43.1234], [2.5690, 43.1245], [2.5700, 43.1250]]
}
}
"""
Quand je fais un POST sur "/api/v1/audio-guides"
Alors le code HTTP de réponse est 201
Et le corps de réponse contient:
| champ | valeur |
| id | UUID généré |
| status | draft |
| creator_id | ID du créateur |
| sequences_count | 0 |
| created_at | Timestamp actuel |
Scénario: Validation du titre (longueur 5-100 caractères)
Étant donné la requête avec titre "ABC"
Quand je fais un POST sur "/api/v1/audio-guides"
Alors le code HTTP de réponse est 400
Et le message d'erreur est "title: doit contenir entre 5 et 100 caractères"
Scénario: Validation de la description (longueur 10-500 caractères)
Étant donné la requête avec description de 8 caractères
Quand je fais un POST sur "/api/v1/audio-guides"
Alors le code HTTP de réponse est 400
Et le message d'erreur est "description: doit contenir entre 10 et 500 caractères"
Plan du Scénario: Validation du mode de déplacement
Étant donné la requête avec mode "<mode>"
Quand je fais un POST sur "/api/v1/audio-guides"
Alors le code HTTP de réponse est <code>
Exemples:
| mode | code |
| pieton | 201 |
| voiture | 201 |
| velo | 201 |
| transport | 201 |
| avion | 400 |
| invalid | 400 |
Scénario: Validation tags (1-3 tags obligatoires)
Étant donné la requête avec 0 tags
Quand je fais un POST sur "/api/v1/audio-guides"
Alors le code HTTP de réponse est 400
Et le message d'erreur est "tags: minimum 1 tag requis, maximum 3"
Scénario: Validation classification âge
Étant donné la requête avec classification_age "invalide"
Quand je fais un POST sur "/api/v1/audio-guides"
Alors le code HTTP de réponse est 400
Et le message d'erreur contient "classification_age: valeurs autorisées [tout_public, 13+, 16+, 18+]"
# Ajout de séquences
Scénario: POST /api/v1/audio-guides/{id}/sequences - Ajout première séquence
Étant donné un audio-guide draft avec ID "ag_123"
Et la requête suivante:
"""json
{
"order": 1,
"title": "Introduction - Point d'accueil",
"audio_file": "base64_encoded_mp3_data",
"gps_point": {
"latitude": 43.1234,
"longitude": 2.5678
},
"rayon_declenchement": 30
}
"""
Quand je fais un POST sur "/api/v1/audio-guides/ag_123/sequences"
Alors le code HTTP de réponse est 201
Et la séquence est créée avec:
| champ | valeur |
| sequence_id | UUID généré |
| order | 1 |
| duration | Calculée depuis audio |
| status | uploaded |
Scénario: Validation format audio (MP3, AAC, M4A uniquement)
Étant donné un audio-guide draft
Et un fichier audio au format WAV
Quand je fais un POST sur "/api/v1/audio-guides/{id}/sequences"
Alors le code HTTP de réponse est 400
Et le message d'erreur est "audio_file: format non supporté. Formats acceptés: MP3, AAC, M4A"
Scénario: Validation taille audio (max 200 MB)
Étant donné un audio-guide draft
Et un fichier audio de 250 MB
Quand je fais un POST sur "/api/v1/audio-guides/{id}/sequences"
Alors le code HTTP de réponse 413
Et le message d'erreur est "audio_file: taille maximale 200 MB dépassée"
Scénario: Validation durée audio (max 15 minutes)
Étant donné un audio-guide draft
Et un fichier audio de 18 minutes
Quand je fais un POST sur "/api/v1/audio-guides/{id}/sequences"
Alors le code HTTP de réponse est 400
Et le message d'erreur est "audio_file: durée maximale 15 minutes dépassée"
Scénario: Point GPS obligatoire sauf mode piéton
Étant donné un audio-guide en mode "voiture"
Et une séquence sans gps_point
Quand je fais un POST sur "/api/v1/audio-guides/{id}/sequences"
Alors le code HTTP de réponse est 400
Et le message d'erreur est "gps_point: obligatoire pour mode voiture"
Scénario: Point GPS optionnel en mode piéton
Étant donné un audio-guide en mode "pieton"
Et une séquence sans gps_point
Quand je fais un POST sur "/api/v1/audio-guides/{id}/sequences"
Alors le code HTTP de réponse est 201
Et la séquence est créée sans point GPS
Plan du Scénario: Rayon de déclenchement par défaut selon mode
Étant donné un audio-guide en mode "<mode>"
Et une séquence sans rayon_declenchement spécifié
Quand je fais un POST sur "/api/v1/audio-guides/{id}/sequences"
Alors le rayon par défaut appliqué est <rayon>
Exemples:
| mode | rayon |
| voiture | 30 |
| velo | 50 |
| transport | 100 |
Scénario: Validation rayon configurable (10-200m)
Étant donné un audio-guide
Et une séquence avec rayon_declenchement 250
Quand je fais un POST sur "/api/v1/audio-guides/{id}/sequences"
Alors le code HTTP de réponse est 400
Et le message d'erreur est "rayon_declenchement: doit être entre 10 et 200 mètres"
Scénario: Nombre maximum de séquences (50)
Étant donné un audio-guide avec 50 séquences
Quand je tente d'ajouter une 51ème séquence
Alors le code HTTP de réponse est 400
Et le message d'erreur est "Maximum 50 séquences par audio-guide atteint"
# Modification et réordonnancement
Scénario: PATCH /api/v1/audio-guides/{id}/sequences/{seq_id} - Modification séquence
Étant donné une séquence existante "seq_456"
Et la requête suivante:
"""json
{
"title": "Introduction - Point d'accueil (édité)",
"rayon_declenchement": 40
}
"""
Quand je fais un PATCH sur "/api/v1/audio-guides/ag_123/sequences/seq_456"
Alors le code HTTP de réponse est 200
Et les champs modifiés sont mis à jour
Et updated_at est mis à jour
Scénario: PUT /api/v1/audio-guides/{id}/sequences/reorder - Réordonnancement
Étant donné un audio-guide avec 5 séquences
Et la requête suivante:
"""json
{
"sequence_orders": [
{"sequence_id": "seq_1", "order": 1},
{"sequence_id": "seq_4", "order": 2},
{"sequence_id": "seq_2", "order": 3},
{"sequence_id": "seq_3", "order": 4},
{"sequence_id": "seq_5", "order": 5}
]
}
"""
Quand je fais un PUT sur "/api/v1/audio-guides/ag_123/sequences/reorder"
Alors le code HTTP de réponse est 200
Et l'ordre des séquences est mis à jour en base
Scénario: DELETE /api/v1/audio-guides/{id}/sequences/{seq_id} - Suppression séquence
Étant donné un audio-guide avec 3 séquences
Quand je fais un DELETE sur "/api/v1/audio-guides/ag_123/sequences/seq_2"
Alors le code HTTP de réponse est 204
Et la séquence est supprimée
Et l'ordre des séquences restantes est réajusté (1, 2)
# Publication et validation
Scénario: POST /api/v1/audio-guides/{id}/publish - Publication (nouveau créateur)
Étant donné un audio-guide draft avec 3 séquences complètes
Et que c'est le 2ème audio-guide du créateur
Quand je fais un POST sur "/api/v1/audio-guides/ag_123/publish"
Alors le code HTTP de réponse est 200
Et le status passe à "pending_moderation"
Et moderation_required est true
Et le corps de réponse contient:
"""json
{
"status": "pending_moderation",
"message": "Votre audio-guide est en cours de validation. Notre équipe le vérifiera sous 24-48h.",
"estimated_validation": "2026-01-24T14:00:00Z"
}
"""
Scénario: Publication directe pour créateur expérimenté (>5 audio-guides validés)
Étant donné un audio-guide draft avec 5 séquences
Et que le créateur a publié 8 audio-guides validés
Et qu'il n'a aucun strike actif
Quand je fais un POST sur "/api/v1/audio-guides/ag_456/publish"
Alors le code HTTP de réponse est 200
Et le status passe à "published"
Et moderation_required est false
Et l'audio-guide est immédiatement visible
Scénario: Validation nombre minimum de séquences (2)
Étant donné un audio-guide draft avec 1 seule séquence
Quand je fais un POST sur "/api/v1/audio-guides/ag_123/publish"
Alors le code HTTP de réponse est 400
Et le message d'erreur est "Minimum 2 séquences requis pour publication"
Scénario: Alerte cohérence - Points GPS trop éloignés
Étant donné un audio-guide en mode "pieton"
Et une séquence au Louvre (Paris)
Et une séquence à Lyon (465 km de distance)
Quand je fais un POST sur "/api/v1/audio-guides/ag_123/publish"
Alors le code HTTP de réponse est 200
Et un warning est retourné:
"""json
{
"status": "pending_moderation",
"warnings": [
{
"type": "distance_incohérence",
"message": "Distance importante entre points (465 km). Vérifiez que le mode 'pieton' est approprié.",
"severity": "warning"
}
]
}
"""
# Gestion des brouillons
Scénario: Sauvegarde automatique brouillon
Étant donné un audio-guide draft non sauvegardé depuis 5 minutes
Quand une modification est apportée via PATCH
Alors le champ updated_at est mis à jour automatiquement
Et le brouillon est sauvegardé en base
Scénario: GET /api/v1/audio-guides/drafts - Liste des brouillons
Étant donné que le créateur a 2 brouillons:
| id | title | sequences_count | updated_at |
| ag_111 | Safari du Paugre | 3 | 2026-01-20 10:00:00 |
| ag_222 | Visite du Louvre | 1 | 2026-01-15 14:30:00 |
Quand je fais un GET sur "/api/v1/audio-guides/drafts"
Alors le code HTTP de réponse est 200
Et le corps de réponse contient les 2 brouillons
Et ils sont triés par updated_at décroissant
Scénario: DELETE /api/v1/audio-guides/{id} - Suppression brouillon
Étant donné un audio-guide draft "ag_123"
Quand je fais un DELETE sur "/api/v1/audio-guides/ag_123"
Alors le code HTTP de réponse est 204
Et l'audio-guide et toutes ses séquences sont supprimés
Et les fichiers audio associés sont marqués pour suppression S3
# Modification audio-guide publié
Scénario: PATCH /api/v1/audio-guides/{id} - Modification métadonnées (publié)
Étant donné un audio-guide publié "ag_789"
Et la requête suivante:
"""json
{
"title": "Safari du Paugre - Version 2",
"description": "Nouvelle description améliorée",
"tags": ["nature", "animaux", "enfants"]
}
"""
Quand je fais un PATCH sur "/api/v1/audio-guides/ag_789"
Alors le code HTTP de réponse est 200
Et les métadonnées sont mises à jour
Et le status reste "published" (pas de revalidation)
Scénario: Interdiction modification mode après publication
Étant donné un audio-guide publié en mode "voiture"
Et la requête avec mode "pieton"
Quand je fais un PATCH sur "/api/v1/audio-guides/{id}"
Alors le code HTTP de réponse est 400
Et le message d'erreur est "mode: impossible de modifier le mode après publication"
Scénario: Interdiction modification GPS après publication
Étant donné un audio-guide publié avec points GPS
Et une tentative de modification des coordonnées GPS
Quand je fais un PATCH sur "/api/v1/audio-guides/{id}/sequences/{seq_id}"
Alors le code HTTP de réponse est 400
Et le message d'erreur est "gps_point: impossible de modifier après publication. Créez une nouvelle version."
# Duplication
Scénario: POST /api/v1/audio-guides/{id}/duplicate - Duplication audio-guide
Étant donné un audio-guide publié "ag_999" avec 12 séquences
Quand je fais un POST sur "/api/v1/audio-guides/ag_999/duplicate"
Alors le code HTTP de réponse est 201
Et un nouvel audio-guide draft est créé
Et le titre est "Safari du Paugre (copie)"
Et toutes les séquences sont copiées avec les mêmes métadonnées
Et les fichiers audio sont référencés (pas de duplication S3)
# Statistiques
Scénario: GET /api/v1/audio-guides/{id}/stats - Statistiques parcours
Étant donné un audio-guide avec les séquences suivantes:
| sequence | duration | gps_point | distance_to_next |
| 1 | 2:15 | (43.1234, 2.5678) | 150m |
| 2 | 3:42 | (43.1245, 2.5690) | 200m |
| 3 | 4:10 | (43.1250, 2.5700) | null |
Quand je fais un GET sur "/api/v1/audio-guides/ag_123/stats"
Alors le code HTTP de réponse est 200
Et le corps de réponse contient:
"""json
{
"sequences_count": 3,
"total_duration": "10:07",
"total_distance": "350m",
"avg_sequence_duration": "3:22"
}
"""
# Gestion zone diffusion
Scénario: Validation zone diffusion (polygon géographique)
Étant donné une zone diffusion de type "polygon"
Et les coordonnées forment un polygon valide (min 3 points)
Quand je fais un POST sur "/api/v1/audio-guides"
Alors la zone est validée avec PostGIS ST_IsValid
Et stockée en type geography
Scénario: Zone diffusion via API Nominatim (ville)
Étant donné une zone diffusion de type "city"
Et la valeur "Paris"
Quand je fais un POST sur "/api/v1/audio-guides"
Alors l'API interroge Nominatim pour récupérer le polygon de Paris
Et le polygon est stocké en base
# Cas d'erreur
Scénario: Authentification requise
Étant donné une requête sans token JWT
Quand je fais un POST sur "/api/v1/audio-guides"
Alors le code HTTP de réponse est 401
Et le message d'erreur est "Authentification requise"
Scénario: Compte non vérifié
Étant donné un créateur avec email_verified: false
Quand je fais un POST sur "/api/v1/audio-guides"
Alors le code HTTP de réponse est 403
Et le message d'erreur est "Vérification email requise pour créer des audio-guides"
Scénario: Modification audio-guide d'un autre créateur (interdite)
Étant donné un audio-guide appartenant au créateur "creator_A"
Et une requête authentifiée par "creator_B"
Quand je fais un PATCH sur "/api/v1/audio-guides/{id}"
Alors le code HTTP de réponse est 403
Et le message d'erreur est "Vous n'êtes pas autorisé à modifier cet audio-guide"
Scénario: Audio-guide inexistant
Quand je fais un GET sur "/api/v1/audio-guides/ag_nonexistant"
Alors le code HTTP de réponse est 404
Et le message d'erreur est "Audio-guide non trouvé"
Scénario: Séquence inexistante
Étant donné un audio-guide "ag_123"
Quand je fais un DELETE sur "/api/v1/audio-guides/ag_123/sequences/seq_nonexistant"
Alors le code HTTP de réponse est 404
Et le message d'erreur est "Séquence non trouvée"
Scénario: Suppression audio-guide avec utilisateurs actifs
Étant donné un audio-guide publié "ag_456"
Et 20 utilisateurs ont une progression active
Quand je fais un DELETE sur "/api/v1/audio-guides/ag_456"
Alors le code HTTP de réponse est 200
Et l'audio-guide est marqué "deleted" (soft delete)
Et les progressions utilisateurs sont archivées pendant 30 jours
Et un warning est retourné: "20 utilisateurs actifs. Progressions archivées 30 jours."