From 0d2b0124ee2dc34dbfaafe64f240c5e29341f250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Munsch?= Date: Fri, 21 Aug 2026 10:41:07 +0200 Subject: [PATCH] docs(architecture): add global architecture documentation for Arcadia API docs(architecture): add request lifecycle documentation for Arcadia API docs(architecture): add execution, transactions, and domain events documentation for Arcadia API docs(architecture): add guide for creating a new use case in Arcadia API docs(exceptions): summarize exception handling and HTTP status codes in Arcadia API docs(tests): outline testing strategy and conventions for Arcadia API style(openapi): correct description formatting in OpenAPI documentation chore: remove outdated exceptions documentation test: remove obsolete tests documentation --- README.md | 51 ++- .../Arcadia/IncoStuffIntelligence.md | 414 +++++++++--------- .../Architecture/01-architecture-globale.md | 95 ++++ .../Architecture/02-cycle-de-vie-requete.md | 164 +++++++ .../03-execution-et-transactions.md | 125 ++++++ .../Architecture/04-guide-nouveau-usecase.md | 144 ++++++ documentation/Conventions/exceptions.md | 62 +++ documentation/Conventions/tests.md | 120 +++++ documentation/Exceptions.md | 60 --- documentation/OpenApi/openapi.yaml | 2 +- documentation/Tests.md | 94 ---- 11 files changed, 943 insertions(+), 388 deletions(-) create mode 100644 documentation/Architecture/01-architecture-globale.md create mode 100644 documentation/Architecture/02-cycle-de-vie-requete.md create mode 100644 documentation/Architecture/03-execution-et-transactions.md create mode 100644 documentation/Architecture/04-guide-nouveau-usecase.md create mode 100644 documentation/Conventions/exceptions.md create mode 100644 documentation/Conventions/tests.md delete mode 100644 documentation/Exceptions.md delete mode 100644 documentation/Tests.md diff --git a/README.md b/README.md index f1f1359..f0d1bba 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,40 @@ # Arcadia API -Arcadia API est une application construite en respectant les principes de la **Clean Architecture** et du **Domain-Driven Design (DDD)**. - -## Sommaire - -- [Arcadia API](#arcadia-api) - - [Sommaire](#sommaire) - - [Architecture](#architecture) - - [Principes d'implémentation](#principes-dimplémentation) - - [Gestion des Exceptions](#gestion-des-exceptions) - - [Tests \& Assurance Qualité](#tests--assurance-qualité) +**Arcadia API** est une API de référence conçue selon les principes de la **Clean Architecture**, du **Domain-Driven Design (DDD)** et de l'**Architecture Hexagonale (Ports & Adapters)**. --- -## Architecture +## 📚 Documentation pour les Développeurs -![Arcadia API Architecture](resources/architecture.jpg) +L'ensemble de la documentation technique et des guides pratiques est organisé dans le dossier [`documentation/`](documentation/) : -L'application est découpée en couches strictes respectant la règle de dépendance vers l'intérieur : +### 🏛️ Architecture & Fonctionnement +- **[01 - Architecture Globale](documentation/Architecture/01-architecture-globale.md)** : Vue d'ensemble des couches (`Domain`, `Application`, `Presentation`, `Infra`, `Shared`), responsabilités et règles de dépendances strictes. +- **[02 - Cycle de Vie d'une Requête (Bullet Tracer)](documentation/Architecture/02-cycle-de-vie-requete.md)** : Traçage pas-à-pas d'une requête HTTP depuis la route jusqu'à la sérialisation JSON. +- **[03 - Exécution, Transactions & Événements](documentation/Architecture/03-execution-et-transactions.md)** : `UsecaseBuilder`, `LaravelExecutor`, exécution transactionnelle et dispatch sécurisé post-commit (`TransactionalDomainEventDispatcher`). +- **[04 - Guide Nouveau Cas d'Usage](documentation/Architecture/04-guide-nouveau-usecase.md)** : Checklist pas-à-pas pour créer une nouvelle fonctionnalité de bout en bout. -1. **Domain** : Le coeur métier de l'application utilisant un language commun. Ne dépend de rien d'autre (**Shared** autorisé). Contient les *Entités*, *Value Objects*, et les interfaces (*Ports*) pour interagir avec le monde extérieur. -2. **Application** : Les *Use Cases*. Orchestre la logique métier en utilisant le Domaine. Ne dépend d'aucune infrastructure ou framework (sauf `Shared`). -3. **Presentation** : Les points d'entrée de l'application (*API Controllers*, *Presenters*). Formate les données pour l'extérieur et gère les requêtes HTTP ou une interface cli. -4. **Infra** : L'implémentation technique (Eloquent, base de données, réseau, etc.). Implémente les interfaces définies par le Domaine. -5. **Shared** : Le socle technique commun (interfaces DDD de base, *Value Objects* génériques comme `Pagination`). +### 📐 Conventions & Standards +- **[Conventions de Tests](documentation/Conventions/tests.md)** : Principes F.I.R.S.T., structure GWT, pyramide de tests, **Test Data Builders** et **Object Mothers**. +- **[Gestion des Exceptions](documentation/Conventions/exceptions.md)** : Matrice de correspondance entre exceptions applicatives/domaine et codes HTTP RFC 7807 (400, 404, 409, 422, 429, 500). -## Principes d'implémentation +--- -- **API Read-only** : Pour l'instant, l'API ne fait qu'exposer des données. Nous utilisons donc le pattern `Directory` (lecture seule) plutôt que `Repository` (lecture/écriture) dans nos Ports de Domaine. -- **Pattern Presenter** : Les Use Cases (dans `Application`) retournent un `Output` agnostique. La couche `Presentation` utilise ensuite des `Presenters` spécifiques (ex: API) pour transformer cet `Output` en une vue exploitable par l'interface finale (JSON). -- **Validation** : La validation stricte du format (ex: HTTP) est gérée dans la couche `Presentation` (via les `FormRequest`). La validation des règles métier est garantie au coeur du `Domain` par les `Value Objects`, qui lèvent des exceptions de type `InvalidArgumentException` traduites en HTTP 422 par le gestionnaire d'erreurs global. +## 🚀 Commandes Utiles -## Gestion des Exceptions +```bash +# Lancer la suite de tests Pest +composer test -Consulter le document [documentation/Exceptions.md](documentation/Exceptions.md) pour la matrice complète de correspondance entre le type d'exception (`DomainException`, `ValidationException`, `NotFoundException`, `ConflictException`) et les codes HTTP RFC 7807 (`400`, `404`, `409`, `422`, `429`, `500`). +# Lancer l'analyse statique PHPStan (Niveau 10) +vendor/bin/phpstan analyse -## Tests & Assurance Qualité +# Vérifier le formattage du code PSR-12 (Pint) +vendor/bin/pint --test -Consulter le document [documentation/Tests.md](documentation/Tests.md) pour la stratégie de test en pyramide (Unit, Integration, Feature, Arch) et les commandes d'exécution avec couverture de code. +# Verifier les conventions et regles de lint (PHP Code Sniffer) +vendor/bin/phpcs +# Corriger automatiquement les conventiosn (PHP Code Beautifier) +vendor/bin/phpcbf +``` diff --git a/documentation/Arcadia/IncoStuffIntelligence.md b/documentation/Arcadia/IncoStuffIntelligence.md index ac73e44..fd27d7a 100644 --- a/documentation/Arcadia/IncoStuffIntelligence.md +++ b/documentation/Arcadia/IncoStuffIntelligence.md @@ -7,273 +7,273 @@ Voici une classification des champs en catégories sémantiques cohérentes pour ## 🏷️ Classification des champs nutritionnels ### 1. **Identification / Métadonnées** -| Champ PHP | Constante | Description | -|---|---|---| -| `nom` | `NOM` | Nom du produit/fiche | -| `numero` | `NUMERO` | Numéro de la fiche | -| `source` | `SOURCE` | Source des données | +| Champ PHP | Constante | Description | +| --------- | --------- | -------------------- | +| `nom` | `NOM` | Nom du produit/fiche | +| `numero` | `NUMERO` | Numéro de la fiche | +| `source` | `SOURCE` | Source des données | --- -### 2. **Valeurs énergétiques — par 100g/100ml (référence `_R`)** -| Champ PHP | Constante | Description | -|---|---|---| -| `tailleReference` | `TAILLE_REF` | Taille de la portion de référence | -| `valEnergKjReference` | `VAL_ENERG_KJ_R` | Énergie en kJ / 100g | -| `valEnergKcalReference` | `VAL_ENERG_KCAL_R` | Énergie en kcal / 100g | +### 2. **Valeurs énergétiques - par 100g/100ml (référence `_R`)** +| Champ PHP | Constante | Description | +| ----------------------- | ------------------ | --------------------------------- | +| `tailleReference` | `TAILLE_REF` | Taille de la portion de référence | +| `valEnergKjReference` | `VAL_ENERG_KJ_R` | Énergie en kJ / 100g | +| `valEnergKcalReference` | `VAL_ENERG_KCAL_R` | Énergie en kcal / 100g | --- -### 3. **Macronutriments — par 100g/100ml (`_R`) + précisions (`_PREC`)** -| Champ PHP | Constante | Description | -|---|---|---| -| `matGrassReference` / `matGrassReferencePrec` | `MAT_GRASS_R` | Matières grasses / 100g | -| `acideGrasSatureReference` / `acideGrasSatureReferencePrec` | `AGS_R` | Acides gras saturés / 100g | -| `glucidesReference` / `glucidesReferencePrec` | `GLUCIDES_R` | Glucides / 100g | -| `sucreReference` / `sucreReferencePrec` | `SUCRE_R` | Sucres / 100g | -| `fibAlimReference` / `fibAlimReferencePrec` | `FIB_ALIM_R` | Fibres alimentaires / 100g | -| `proteineReference` / `proteineReferencePrec` | `PROTEINE_R` | Protéines / 100g | -| `selReference` / `selReferencePrec` | `SEL_R` | Sel / 100g | -| `sodiumReference` | `SODIUM_R` | Sodium / 100g | +### 3. **Macronutriments - par 100g/100ml (`_R`) + précisions (`_PREC`)** +| Champ PHP | Constante | Description | +| ----------------------------------------------------------- | ------------- | -------------------------- | +| `matGrassReference` / `matGrassReferencePrec` | `MAT_GRASS_R` | Matières grasses / 100g | +| `acideGrasSatureReference` / `acideGrasSatureReferencePrec` | `AGS_R` | Acides gras saturés / 100g | +| `glucidesReference` / `glucidesReferencePrec` | `GLUCIDES_R` | Glucides / 100g | +| `sucreReference` / `sucreReferencePrec` | `SUCRE_R` | Sucres / 100g | +| `fibAlimReference` / `fibAlimReferencePrec` | `FIB_ALIM_R` | Fibres alimentaires / 100g | +| `proteineReference` / `proteineReferencePrec` | `PROTEINE_R` | Protéines / 100g | +| `selReference` / `selReferencePrec` | `SEL_R` | Sel / 100g | +| `sodiumReference` | `SODIUM_R` | Sodium / 100g | --- -### 4. **Fruits, légumes, noix — par 100g/100ml (`_R`)** -| Champ PHP | Constante | Description | -|---|---|---| -| `fruitsReference` | `FRUITS_R` | Teneur en fruits / 100g | +### 4. **Fruits, légumes, noix - par 100g/100ml (`_R`)** +| Champ PHP | Constante | Description | +| -------------------------- | --------------------- | ------------------------------- | +| `fruitsReference` | `FRUITS_R` | Teneur en fruits / 100g | | `fruitsCoqueSechReference` | `FRUITS_COQUE_SECH_R` | Fruits à coque et séchés / 100g | -| `pourcentFruits` | `POURCENT_FRUITS` | % fruits (global) | +| `pourcentFruits` | `POURCENT_FRUITS` | % fruits (global) | --- -### 5. **Valeurs nutritionnelles — par portion (`_P`) + précisions (`_PREC`)** -| Champ PHP | Constante | Description | -|---|---|---| -| `taillePortion` | `TAILLE_PORT` | Taille de la portion | -| `valEnergKjPortion` | `VAL_ENERG_KJ_P` | Énergie kJ / portion | -| `valEnergKcalPortion` | `VAL_ENERG_KCAL_P` | Énergie kcal / portion | -| `matGrassPortion` / `matGrassPortionPrec` | `MAT_GRASS_P` | Matières grasses / portion | -| `acideGrasSaturePortion` / `acideGrasSaturePortionPrec` | `AGS_P` | Acides gras saturés / portion | -| `glucidesPortion` / `glucidesPortionPrec` | `GLUCIDES_P` | Glucides / portion | -| `sucrePortion` / `sucrePortionPrec` | `SUCRE_P` | Sucres / portion | -| `fibAlimPortion` / `fibAlimPortionPrec` | `FIB_ALIM_P` | Fibres / portion | -| `proteinePortion` / `proteinePortionPrec` | `PROTEINE_P` | Protéines / portion | -| `sodiumPortion` | `SODIUM_P` | Sodium / portion | -| `selPortion` / `selPortionPrec` | `SEL_P` | Sel / portion | -| `fruitsPortion` | `FRUITS_P` | Fruits / portion | -| `fruitsCoqueSechPortion` | `FRUITS_COQUE_SECH_P` | Fruits à coque et séchés / portion | +### 5. **Valeurs nutritionnelles - par portion (`_P`) + précisions (`_PREC`)** +| Champ PHP | Constante | Description | +| ------------------------------------------------------- | --------------------- | ---------------------------------- | +| `taillePortion` | `TAILLE_PORT` | Taille de la portion | +| `valEnergKjPortion` | `VAL_ENERG_KJ_P` | Énergie kJ / portion | +| `valEnergKcalPortion` | `VAL_ENERG_KCAL_P` | Énergie kcal / portion | +| `matGrassPortion` / `matGrassPortionPrec` | `MAT_GRASS_P` | Matières grasses / portion | +| `acideGrasSaturePortion` / `acideGrasSaturePortionPrec` | `AGS_P` | Acides gras saturés / portion | +| `glucidesPortion` / `glucidesPortionPrec` | `GLUCIDES_P` | Glucides / portion | +| `sucrePortion` / `sucrePortionPrec` | `SUCRE_P` | Sucres / portion | +| `fibAlimPortion` / `fibAlimPortionPrec` | `FIB_ALIM_P` | Fibres / portion | +| `proteinePortion` / `proteinePortionPrec` | `PROTEINE_P` | Protéines / portion | +| `sodiumPortion` | `SODIUM_P` | Sodium / portion | +| `selPortion` / `selPortionPrec` | `SEL_P` | Sel / portion | +| `fruitsPortion` | `FRUITS_P` | Fruits / portion | +| `fruitsCoqueSechPortion` | `FRUITS_COQUE_SECH_P` | Fruits à coque et séchés / portion | --- ### 6. **Pourcentages des apports de référence (`POURCENT_APPORT_REF_*`)** -| Champ PHP | Constante | Description | -|---|---|---| -| `pourcentApportRefE14P` | `POURCENT_APPORT_REF_E14_P` | % AR énergie E14 / portion | -| `pourcentApportRefKjoP` | `POURCENT_APPORT_REF_KJO_P` | % AR énergie kJ / portion | -| `pourcentApportRefFatP` | `POURCENT_APPORT_REF_FAT_P` | % AR matières grasses / portion | +| Champ PHP | Constante | Description | +| -------------------------- | ------------------------------ | -------------------------------- | +| `pourcentApportRefE14P` | `POURCENT_APPORT_REF_E14_P` | % AR énergie E14 / portion | +| `pourcentApportRefKjoP` | `POURCENT_APPORT_REF_KJO_P` | % AR énergie kJ / portion | +| `pourcentApportRefFatP` | `POURCENT_APPORT_REF_FAT_P` | % AR matières grasses / portion | | `pourcentApportRefFatsatP` | `POURCENT_APPORT_REF_FATSAT_P` | % AR graisses saturées / portion | -| `pourcentApportRefChovalP` | `POURCENT_APPORT_REF_CHOVAL_P` | % AR glucides / portion | -| `pourcentApportRefSugarP` | `POURCENT_APPORT_REF_SUGAR_P` | % AR sucres / portion | -| `pourcentApportRefProP` | `POURCENT_APPORT_REF_PRO_P` | % AR protéines / portion | -| `pourcentApportRefSalteqP` | `POURCENT_APPORT_REF_SALTEQ_P` | % AR sel / portion | +| `pourcentApportRefChovalP` | `POURCENT_APPORT_REF_CHOVAL_P` | % AR glucides / portion | +| `pourcentApportRefSugarP` | `POURCENT_APPORT_REF_SUGAR_P` | % AR sucres / portion | +| `pourcentApportRefProP` | `POURCENT_APPORT_REF_PRO_P` | % AR protéines / portion | +| `pourcentApportRefSalteqP` | `POURCENT_APPORT_REF_SALTEQ_P` | % AR sel / portion | --- ### 7. **Nutri-Score** -| Champ PHP | Constante | Description | -|---|---|---| -| `composanteN` | `COMPOSANTE_N` | Composante négative (énergie, graisses, sucres, sel) | -| `composanteN2022` | `COMPOSANTE_N_2022` | Composante négative — algorithme 2022 | -| `composanteP` | `COMPOSANTE_P` | Composante positive (fibres, protéines, fruits) | -| `composanteP2022` | `COMPOSANTE_P_2022` | Composante positive — algorithme 2022 | -| `scoreNutri` | `SCORE_NUTRI` | Score Nutri-Score brut | -| `scoreNutri2022` | `SCORE_NUTRI_2022` | Score Nutri-Score 2022 | -| `classement` | `CLASSEMENT` | Lettre Nutri-Score (A–E) | -| `nutriScore2022` | `NUTRI_SCORE_2022` | Nutri-Score lettre — algorithme 2022 | -| `nutriAArt` | `NUTRI_A_ART` | Flag article Nutri-Score A | +| Champ PHP | Constante | Description | +| ----------------- | ------------------- | ---------------------------------------------------- | +| `composanteN` | `COMPOSANTE_N` | Composante négative (énergie, graisses, sucres, sel) | +| `composanteN2022` | `COMPOSANTE_N_2022` | Composante négative - algorithme 2022 | +| `composanteP` | `COMPOSANTE_P` | Composante positive (fibres, protéines, fruits) | +| `composanteP2022` | `COMPOSANTE_P_2022` | Composante positive - algorithme 2022 | +| `scoreNutri` | `SCORE_NUTRI` | Score Nutri-Score brut | +| `scoreNutri2022` | `SCORE_NUTRI_2022` | Score Nutri-Score 2022 | +| `classement` | `CLASSEMENT` | Lettre Nutri-Score (A–E) | +| `nutriScore2022` | `NUTRI_SCORE_2022` | Nutri-Score lettre - algorithme 2022 | +| `nutriAArt` | `NUTRI_A_ART` | Flag article Nutri-Score A | --- ### 8. **Points de calcul du Nutri-Score** -| Champ PHP | Constante | Description | -|---|---|---| -| `pointsEnergie` | `POINTS_ENERGIE` | Points énergie | -| `pointsAcideGrasSature` | `POINTS_AGS` | Points acides gras saturés | -| `pointsSucres` | `POINTS_SUCRES` | Points sucres | -| `pointsSel` | `POINTS_SEL` | Points sel | -| `pointsFibres` | `POINTS_FIBRES` | Points fibres | -| `pointsProteines` | `POINTS_PROTEINES` | Points protéines | +| Champ PHP | Constante | Description | +| ------------------------------ | ------------------ | -------------------------------- | +| `pointsEnergie` | `POINTS_ENERGIE` | Points énergie | +| `pointsAcideGrasSature` | `POINTS_AGS` | Points acides gras saturés | +| `pointsSucres` | `POINTS_SUCRES` | Points sucres | +| `pointsSel` | `POINTS_SEL` | Points sel | +| `pointsFibres` | `POINTS_FIBRES` | Points fibres | +| `pointsProteines` | `POINTS_PROTEINES` | Points protéines | | `pointsFruitsLegumesNoixFrais` | `POINTS_FNL_FRAIS` | Points fruits/noix/légumes frais | --- ### 9. **FNL (Fruits, Noix, Légumes)** -| Champ PHP | Constante | Description | -|---|---|---| -| `fruitLegumeNoixFrais` | `FNL_FRAIS` | Teneur FNL frais / 100g | -| `fruitLegumeNoixFraisPortion` | `FNL_FRAIS_PORTION` | Teneur FNL frais / portion | -| `pourcentFruitsLegumesNoixFrais` | `POURCENT_FNL_FRAIS` | % FNL frais | +| Champ PHP | Constante | Description | +| -------------------------------- | -------------------- | -------------------------- | +| `fruitLegumeNoixFrais` | `FNL_FRAIS` | Teneur FNL frais / 100g | +| `fruitLegumeNoixFraisPortion` | `FNL_FRAIS_PORTION` | Teneur FNL frais / portion | +| `pourcentFruitsLegumesNoixFrais` | `POURCENT_FNL_FRAIS` | % FNL frais | --- ### 10. **Ratio / Indicateurs complémentaires** -| Champ PHP | Constante | Description | -|---|---|---| -| `pSurL` | `P_SUR_L` | Ratio P/L (protéines sur lipides) | +| Champ PHP | Constante | Description | +| ------------- | -------------- | ------------------------------------------- | +| `pSurL` | `P_SUR_L` | Ratio P/L (protéines sur lipides) | | `viandeRouge` | `VIANDE_ROUGE` | Flag viande rouge (impact Nutri-Score 2022) | --- ### 11. **Micronutriments détaillés (`NUT_*`)** -Cette famille regroupe les vitamines, minéraux, acides aminés et acides gras détaillés — typiquement issus de bases comme **Ciqual** ou **FoodData Central**. +Cette famille regroupe les vitamines, minéraux, acides aminés et acides gras détaillés - typiquement issus de bases comme **Ciqual** ou **FoodData Central**. #### 11a. Minéraux & Oligo-éléments -| Champ PHP | Constante | -|---|---| -| `nutCa` | `NUT_CA` — Calcium | -| `nutMg` | `NUT_MG` — Magnésium | -| `nutK` | `NUT_K` — Potassium | -| `nutP` | `NUT_P` — Phosphore | -| `nutCld` | `NUT_CLD` — Chlorure | -| `nutFe` | `NUT_FE` — Fer | -| `nutZn` | `NUT_ZN` — Zinc | -| `nutCu` | `NUT_CU` — Cuivre | -| `nutMn` | `NUT_MN` — Manganèse | -| `nutSe` | `NUT_SE` — Sélénium | -| `nutId` | `NUT_ID` — Iode | -| `nutCr` | `NUT_CR` — Chrome | -| `nutMo` | `NUT_MO` — Molybdène | -| `nutS6` | `NUT_S6` — Soufre | -| `nutNitra` | `NUT_NITRA` — Nitrates | +| Champ PHP | Constante | +| ---------- | ---------------------- | +| `nutCa` | `NUT_CA` - Calcium | +| `nutMg` | `NUT_MG` - Magnésium | +| `nutK` | `NUT_K` - Potassium | +| `nutP` | `NUT_P` - Phosphore | +| `nutCld` | `NUT_CLD` - Chlorure | +| `nutFe` | `NUT_FE` - Fer | +| `nutZn` | `NUT_ZN` - Zinc | +| `nutCu` | `NUT_CU` - Cuivre | +| `nutMn` | `NUT_MN` - Manganèse | +| `nutSe` | `NUT_SE` - Sélénium | +| `nutId` | `NUT_ID` - Iode | +| `nutCr` | `NUT_CR` - Chrome | +| `nutMo` | `NUT_MO` - Molybdène | +| `nutS6` | `NUT_S6` - Soufre | +| `nutNitra` | `NUT_NITRA` - Nitrates | #### 11b. Vitamines -| Champ PHP | Constante | -|---|---| -| `nutVita` | `NUT_VITA` — Vitamine A | -| `nutVitb6` | `NUT_VITB6` — Vitamine B6 | -| `nutVitb12` | `NUT_VITB12` — Vitamine B12 | -| `nutVitc` | `NUT_VITC` — Vitamine C | -| `nutVitd` | `NUT_VITD` — Vitamine D | -| `nutVite` | `NUT_VITE` — Vitamine E | -| `nutVitk` | `NUT_VITK` — Vitamine K | -| `nutChocal` | `NUT_CHOCAL` — Cholécalciférol (vit. D3) | -| `nutNia` | `NUT_NIA` — Niacine (B3) | -| `nutRibf` | `NUT_RIBF` — Riboflavine (B2) | -| `nutThia` | `NUT_THIA` — Thiamine (B1) | -| `nutPantac` | `NUT_PANTAC` — Acide pantothénique (B5) | -| `nutBiot` | `NUT_BIOT` — Biotine (B8) | -| `nutFoldfe` | `NUT_FOLDFE` — Folates équivalents (B9) | +| Champ PHP | Constante | +| ----------- | ---------------------------------------- | +| `nutVita` | `NUT_VITA` - Vitamine A | +| `nutVitb6` | `NUT_VITB6` - Vitamine B6 | +| `nutVitb12` | `NUT_VITB12` - Vitamine B12 | +| `nutVitc` | `NUT_VITC` - Vitamine C | +| `nutVitd` | `NUT_VITD` - Vitamine D | +| `nutVite` | `NUT_VITE` - Vitamine E | +| `nutVitk` | `NUT_VITK` - Vitamine K | +| `nutChocal` | `NUT_CHOCAL` - Cholécalciférol (vit. D3) | +| `nutNia` | `NUT_NIA` - Niacine (B3) | +| `nutRibf` | `NUT_RIBF` - Riboflavine (B2) | +| `nutThia` | `NUT_THIA` - Thiamine (B1) | +| `nutPantac` | `NUT_PANTAC` - Acide pantothénique (B5) | +| `nutBiot` | `NUT_BIOT` - Biotine (B8) | +| `nutFoldfe` | `NUT_FOLDFE` - Folates équivalents (B9) | #### 11c. Glucides détaillés -| Champ PHP | Constante | -|---|---| -| `nutStarch` | `NUT_STARCH` — Amidon | -| `nutMaltdex` | `NUT_MALTDEX` — Maltodextrines | -| `nutSucs` | `NUT_SUCS` — Saccharose | -| `nutGlus` | `NUT_GLUS` — Glucose | -| `nutLacs` | `NUT_LACS` — Lactose | -| `nutMals` | `NUT_MALS` — Maltose | -| `nutGalfb` | `NUT_GALFB` — Galactose | -| `nutGHc` | `NUT_G_HC` — Glucides hydrates de carbone | -| `nutFrufb` | `NUT_FRUFB` — Fructose lié (fibre) | -| `nutFrus` | `NUT_FRUS` — Fructose libre | -| `nutOlsac` | `NUT_OLSAC` — Oligosaccharides | -| `nutPolyl` | `NUT_POLYL` — Polyols | -| `nutFibsol` | `NUT_FIBSOL` — Fibres solubles | -| `nutInotl` | `NUT_INOTL` — Inositol | +| Champ PHP | Constante | +| ------------ | ----------------------------------------- | +| `nutStarch` | `NUT_STARCH` - Amidon | +| `nutMaltdex` | `NUT_MALTDEX` - Maltodextrines | +| `nutSucs` | `NUT_SUCS` - Saccharose | +| `nutGlus` | `NUT_GLUS` - Glucose | +| `nutLacs` | `NUT_LACS` - Lactose | +| `nutMals` | `NUT_MALS` - Maltose | +| `nutGalfb` | `NUT_GALFB` - Galactose | +| `nutGHc` | `NUT_G_HC` - Glucides hydrates de carbone | +| `nutFrufb` | `NUT_FRUFB` - Fructose lié (fibre) | +| `nutFrus` | `NUT_FRUS` - Fructose libre | +| `nutOlsac` | `NUT_OLSAC` - Oligosaccharides | +| `nutPolyl` | `NUT_POLYL` - Polyols | +| `nutFibsol` | `NUT_FIBSOL` - Fibres solubles | +| `nutInotl` | `NUT_INOTL` - Inositol | #### 11d. Lipides / Acides gras détaillés -> Nomenclature lipidique standard (IUPAC / Ciqual) — `FXXdY` = chaîne carbonée XX, Y insaturations +> Nomenclature lipidique standard (IUPAC / Ciqual) - `FXXdY` = chaîne carbonée XX, Y insaturations -| Champ PHP | Constante | Nom usuel | -|---|---|---| -| `nutAgt` | `NUT_AGT` | Acides gras trans | -| `nutChol` | `NUT_CHOL` | Cholestérol | -| `nutFd` | `NUT_FD` | Lipides totaux | -| `nutFpalm` | `NUT_FPALM` | Palmitoleïque (C16:1) | -| `nutF16d0` | `NUT_F16D0` | Acide palmitique (C16:0) | -| `nutFsteariq` | `NUT_FSTEARIQ` | Acide stéarique (C18:0) | -| `nutFoleiq` | `NUT_FOLEIQ` | Acide oléique (C18:1, ω-9) | -| `nutF18d` | `NUT_F18D` | C18 insaturé (générique) | -| `nutF18d2cn6` | `NUT_F18D2CN6` | Acide linoléique (C18:2, ω-6) | -| `nutF18d3n3` | `NUT_F18D3N3` | Acide α-linolénique (C18:3, ω-3) | -| `nutF20d4` | `NUT_F20D4` | Acide arachidonique (C20:4) | -| `nutF20d5n3` | `NUT_F20D5N3` | EPA (C20:5, ω-3) | -| `nutF22d6n3` | `NUT_F22D6N3` | DHA (C22:6, ω-3) | -| `nutF11eico` | `NUT_F11EICO` | Acide gondoïque (C20:1) | -| `nutFarachidiq` | `NUT_FARACHIDIQ` | Acide arachidique (C20:0) | -| `nutFeruciq` | `NUT_FERUCIQ` | Acide érucique (C22:1) | -| `nutFlauriq` | `NUT_FLAURIQ` | Acide laurique (C12:0) | -| `nutFmyristiq` | `NUT_FMYRISTIQ` | Acide myristique (C14:0) | -| `nutFbutyriq` | `NUT_FBUTYRIQ` | Acide butyrique (C4:0) | -| `nutFcaproiq` | `NUT_FCAPROIQ` | Acide caproïque (C6:0) | -| `nutFcapryliq` | `NUT_FCAPRYLIQ` | Acide caprylique (C8:0) | -| `nutFcapriq` | `NUT_FCAPRIQ` | Acide caprique (C10:0) | -| `nutFapun3` | `NUT_FAPUN3` | AG polyinsaturés ω-3 | -| `nutFapun6` | `NUT_FAPUN6` | AG polyinsaturés ω-6 | -| `nutFapun9` | `NUT_FAPUN9` | AG polyinsaturés ω-9 | -| `nutFapucis` | `NUT_FAPUCIS` | AG polyinsaturés cis | -| `nutFapulc` | `NUT_FAPULC` | AG polyinsaturés longue chaîne | -| `nutFamscis` | `NUT_FAMSCIS` | AG monoinsaturés cis | -| `nutFacetiq` | `NUT_FACETIQ` | Acide acétique (C2:0) | -| `nutFpropioniq` | `NUT_FPROPIONIQ` | Acide propionique (C3:0) | -| `nutFvaleriq` | `NUT_FVALERIQ` | Acide valérique (C5:0) | -| `nutFcarboceriq` | `NUT_FCARBOCERIQ` | Acide carbocérique (C26:0) | -| `nutFbeheniq` | `NUT_FBEHENIQ` | Acide béhénique (C22:0) | -| `nutFlignoceriq` | `NUT_FLIGNOCERIQ` | Acide lignocérique (C24:0) | -| `nutFnervoniq` | `NUT_FNERVONIQ` | Acide nervonique (C24:1) | -| `nutFlaceroiq` | `NUT_FLACEROIQ` | Acide lacéroïque (C32:0) | -| `nutFmelissiq` | `NUT_FMELISSIQ` | Acide mélissique (C30:0) | -| `nutFmontaniq` | `NUT_FMONTANIQ` | Acide montanique (C28:0) | -| `nutFcerotiq` | `NUT_FCEROTIQ` | Acide cérotique (C26:0 alt.) | -| `nutFpsylliq` | `NUT_FPSYLLIQ` | Acide psyllique | -| `nutFgeddiq` | `NUT_FGEDDIQ` | Acide geddique (C20:1) | -| `nutFmargariq` | `NUT_FMARGARIQ` | Acide margarique (C17:0) | -| `nutFenatiq` | `NUT_FENATIQ` | Acide énanthique (C7:0) | -| `nutFformiq` | `NUT_FFORMIQ` | Acide formique (C1:0) | -| `nutFhyeniq` | `NUT_FHYENIQ` | Acide hyénique | -| `nutFrufb` | `NUT_FRUFB` | *(déjà classé glucides)* | -| `nutFrus` | `NUT_FRUS` | *(déjà classé glucides)* | -| `nutFpelargoniq` | `NUT_FPELARGONIQ` | Acide pélargonique (C9:0) | -| `nutFundecyliq` | `NUT_FUNDECYLIQ` | Acide undécylique (C11:0) | -| `nutFpentadecyliq` | `NUT_FPENTADECYLIQ` | Acide pentadécylique (C15:0) | -| `nutFtridecyliq` | `NUT_FTRIDECYLIQ` | Acide tridécylique (C13:0) | -| `nutFtricosiliq` | `NUT_FTRICOSILIQ` | Acide tétracosanoïque (C23:0) | -| `nutFnonacosyliq` | `NUT_FNONACOSYLIQ` | Acide nonacosylique (C29:0) | -| `nutFnonadylique` | `NUT_FNONADYLIQUE` | Acide nonadylique | -| `nutFheneicosyliq` | `NUT_FHENEICOSYLIQ` | Acide héneicosylique (C21:0) | -| `nutFhentriacontyliq` | `NUT_FHENTRIACONTYLIQ` | C31:0 | -| `nutFheptatriacontyliq` | `NUT_FHEPTATRIACONTYLIQ` | C37:0 | -| `nutFhextriacontyliq` | `NUT_FHEXTRIACONTYLIQ` | C36:0 | -| `nutFoctatriacontyliq` | `NUT_FOCTATRIACONTYLIQ` | C38:0 | -| `nutF19d` | `NUT_F19D` | C19 insaturé | -| `nutFoldfe` | `NUT_FOLDFE` | *(déjà classé vitamines — folates)* | +| Champ PHP | Constante | Nom usuel | +| ----------------------- | ------------------------ | ----------------------------------- | +| `nutAgt` | `NUT_AGT` | Acides gras trans | +| `nutChol` | `NUT_CHOL` | Cholestérol | +| `nutFd` | `NUT_FD` | Lipides totaux | +| `nutFpalm` | `NUT_FPALM` | Palmitoleïque (C16:1) | +| `nutF16d0` | `NUT_F16D0` | Acide palmitique (C16:0) | +| `nutFsteariq` | `NUT_FSTEARIQ` | Acide stéarique (C18:0) | +| `nutFoleiq` | `NUT_FOLEIQ` | Acide oléique (C18:1, ω-9) | +| `nutF18d` | `NUT_F18D` | C18 insaturé (générique) | +| `nutF18d2cn6` | `NUT_F18D2CN6` | Acide linoléique (C18:2, ω-6) | +| `nutF18d3n3` | `NUT_F18D3N3` | Acide α-linolénique (C18:3, ω-3) | +| `nutF20d4` | `NUT_F20D4` | Acide arachidonique (C20:4) | +| `nutF20d5n3` | `NUT_F20D5N3` | EPA (C20:5, ω-3) | +| `nutF22d6n3` | `NUT_F22D6N3` | DHA (C22:6, ω-3) | +| `nutF11eico` | `NUT_F11EICO` | Acide gondoïque (C20:1) | +| `nutFarachidiq` | `NUT_FARACHIDIQ` | Acide arachidique (C20:0) | +| `nutFeruciq` | `NUT_FERUCIQ` | Acide érucique (C22:1) | +| `nutFlauriq` | `NUT_FLAURIQ` | Acide laurique (C12:0) | +| `nutFmyristiq` | `NUT_FMYRISTIQ` | Acide myristique (C14:0) | +| `nutFbutyriq` | `NUT_FBUTYRIQ` | Acide butyrique (C4:0) | +| `nutFcaproiq` | `NUT_FCAPROIQ` | Acide caproïque (C6:0) | +| `nutFcapryliq` | `NUT_FCAPRYLIQ` | Acide caprylique (C8:0) | +| `nutFcapriq` | `NUT_FCAPRIQ` | Acide caprique (C10:0) | +| `nutFapun3` | `NUT_FAPUN3` | AG polyinsaturés ω-3 | +| `nutFapun6` | `NUT_FAPUN6` | AG polyinsaturés ω-6 | +| `nutFapun9` | `NUT_FAPUN9` | AG polyinsaturés ω-9 | +| `nutFapucis` | `NUT_FAPUCIS` | AG polyinsaturés cis | +| `nutFapulc` | `NUT_FAPULC` | AG polyinsaturés longue chaîne | +| `nutFamscis` | `NUT_FAMSCIS` | AG monoinsaturés cis | +| `nutFacetiq` | `NUT_FACETIQ` | Acide acétique (C2:0) | +| `nutFpropioniq` | `NUT_FPROPIONIQ` | Acide propionique (C3:0) | +| `nutFvaleriq` | `NUT_FVALERIQ` | Acide valérique (C5:0) | +| `nutFcarboceriq` | `NUT_FCARBOCERIQ` | Acide carbocérique (C26:0) | +| `nutFbeheniq` | `NUT_FBEHENIQ` | Acide béhénique (C22:0) | +| `nutFlignoceriq` | `NUT_FLIGNOCERIQ` | Acide lignocérique (C24:0) | +| `nutFnervoniq` | `NUT_FNERVONIQ` | Acide nervonique (C24:1) | +| `nutFlaceroiq` | `NUT_FLACEROIQ` | Acide lacéroïque (C32:0) | +| `nutFmelissiq` | `NUT_FMELISSIQ` | Acide mélissique (C30:0) | +| `nutFmontaniq` | `NUT_FMONTANIQ` | Acide montanique (C28:0) | +| `nutFcerotiq` | `NUT_FCEROTIQ` | Acide cérotique (C26:0 alt.) | +| `nutFpsylliq` | `NUT_FPSYLLIQ` | Acide psyllique | +| `nutFgeddiq` | `NUT_FGEDDIQ` | Acide geddique (C20:1) | +| `nutFmargariq` | `NUT_FMARGARIQ` | Acide margarique (C17:0) | +| `nutFenatiq` | `NUT_FENATIQ` | Acide énanthique (C7:0) | +| `nutFformiq` | `NUT_FFORMIQ` | Acide formique (C1:0) | +| `nutFhyeniq` | `NUT_FHYENIQ` | Acide hyénique | +| `nutFrufb` | `NUT_FRUFB` | *(déjà classé glucides)* | +| `nutFrus` | `NUT_FRUS` | *(déjà classé glucides)* | +| `nutFpelargoniq` | `NUT_FPELARGONIQ` | Acide pélargonique (C9:0) | +| `nutFundecyliq` | `NUT_FUNDECYLIQ` | Acide undécylique (C11:0) | +| `nutFpentadecyliq` | `NUT_FPENTADECYLIQ` | Acide pentadécylique (C15:0) | +| `nutFtridecyliq` | `NUT_FTRIDECYLIQ` | Acide tridécylique (C13:0) | +| `nutFtricosiliq` | `NUT_FTRICOSILIQ` | Acide tétracosanoïque (C23:0) | +| `nutFnonacosyliq` | `NUT_FNONACOSYLIQ` | Acide nonacosylique (C29:0) | +| `nutFnonadylique` | `NUT_FNONADYLIQUE` | Acide nonadylique | +| `nutFheneicosyliq` | `NUT_FHENEICOSYLIQ` | Acide héneicosylique (C21:0) | +| `nutFhentriacontyliq` | `NUT_FHENTRIACONTYLIQ` | C31:0 | +| `nutFheptatriacontyliq` | `NUT_FHEPTATRIACONTYLIQ` | C37:0 | +| `nutFhextriacontyliq` | `NUT_FHEXTRIACONTYLIQ` | C36:0 | +| `nutFoctatriacontyliq` | `NUT_FOCTATRIACONTYLIQ` | C38:0 | +| `nutF19d` | `NUT_F19D` | C19 insaturé | +| `nutFoldfe` | `NUT_FOLDFE` | *(déjà classé vitamines - folates)* | #### 11e. Acides aminés & protéines spécifiques -| Champ PHP | Constante | Description | -|---|---|---| -| `nutAae` | `NUT_AAE` | Acides aminés essentiels (total) | -| `nutCasn` | `NUT_CASN` | Caséines | -| `nutWhey` | `NUT_WHEY` | Protéines de lactosérum | +| Champ PHP | Constante | Description | +| --------- | ---------- | -------------------------------- | +| `nutAae` | `NUT_AAE` | Acides aminés essentiels (total) | +| `nutCasn` | `NUT_CASN` | Caséines | +| `nutWhey` | `NUT_WHEY` | Protéines de lactosérum | #### 11f. Autres composés bioactifs / spéciaux -| Champ PHP | Constante | Description | -|---|---|---| -| `nutCholn` | `NUT_CHOLN` | Choline | -| `nutCarnitine` | `NUT_CARNITINE` | Carnitine | -| `nutTau` | `NUT_TAU` | Taurine | +| Champ PHP | Constante | Description | +| --------------- | ---------------- | ----------- | +| `nutCholn` | `NUT_CHOLN` | Choline | +| `nutCarnitine` | `NUT_CARNITINE` | Carnitine | +| `nutTau` | `NUT_TAU` | Taurine | | `nutNucleotide` | `NUT_NUCLEOTIDE` | Nucléotides | --- ## ⚠️ Points d'attention -1. **Doublon détecté** : `'acideGrasSaturePortion' => 'AGS_P'` est déclaré **deux fois** — l'une des deux entrées sera silencieusement ignorée par PHP. +1. **Doublon détecté** : `'acideGrasSaturePortion' => 'AGS_P'` est déclaré **deux fois** - l'une des deux entrées sera silencieusement ignorée par PHP. -2. **Champs `_PREC`** : Les suffixes `_PREC` semblent indiquer la **précision de mesure** (ex. `<`, `~`, tracé) — à documenter explicitement. +2. **Champs `_PREC`** : Les suffixes `_PREC` semblent indiquer la **précision de mesure** (ex. `<`, `~`, tracé) - à documenter explicitement. -3. **Champs MISSING** : Nombreux champs présents dans le référentiel INCO mais absents du mapping — notamment toute la série `POURCENT_APPORT_REF_*_R` (apports de référence pour 100g) et certains AG comme `FATSAT_R`, `CHOVAL_R`, `SUGAR_R`. +3. **Champs MISSING** : Nombreux champs présents dans le référentiel INCO mais absents du mapping - notamment toute la série `POURCENT_APPORT_REF_*_R` (apports de référence pour 100g) et certains AG comme `FATSAT_R`, `CHOVAL_R`, `SUGAR_R`. -4. **`classement` / `nutriscore`** : Vous notez vous-même que `CLASSEMENT` ne devrait pas être dans `INCO_COMPO` — à externaliser dans un objet Nutri-Score dédié. +4. **`classement` / `nutriscore`** : Vous notez vous-même que `CLASSEMENT` ne devrait pas être dans `INCO_COMPO` - à externaliser dans un objet Nutri-Score dédié. diff --git a/documentation/Architecture/01-architecture-globale.md b/documentation/Architecture/01-architecture-globale.md new file mode 100644 index 0000000..15cca8a --- /dev/null +++ b/documentation/Architecture/01-architecture-globale.md @@ -0,0 +1,95 @@ +# Architecture Globale - Arcadia API + +Ce document présente l'architecture logicielle d'**Arcadia API** à destination des développeurs. + +--- + +## 🏛️ Les 3 Piliers Architecturaux + +Arcadia API combine 3 paradigmes éprouvés pour créer une base de code hautement maintenable, testable et découplée du framework : + +1. **Clean Architecture (Robert C. Martin)** : Séparation en couches concentriques avec **règle de dépendance stricte vers l'intérieur**. +2. **Domain-Driven Design (Eric Evans)** : Modèle métier riche et expressif (Entités, Value Objects, Agrégats, Événements de Domaine), indépendant de tout détail technique. +3. **Architecture Hexagonale (Alistair Cockburn)** : Ports (interfaces dans le Domaine) et Adaptateurs (implémentations techniques dans l'Infrastructure). + +``` +┌─────────────────────────────────────────────────────────────┐ +│ PRESENTATION │ +│ (API Controllers, FormRequests, Presenters, Views, DTOs) │ +├──────────────────────────────┬──────────────────────────────┤ +│ APPLICATION │ INFRA │ +│ (Use Cases, Input, Output) │ (Eloquent, DB, Cache, Logs) │ +├──────────────────────────────┴──────────────────────────────┤ +│ DOMAIN │ +│ (Entities, Value Objects, Ports/Interfaces, Events) │ +├─────────────────────────────────────────────────────────────┤ +│ SHARED │ +│ (Socle DDD générique, UsecaseBuilder, Pagination) │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## 🎯 Rôle et Responsabilité de Chaque Couche + +### 1. `Domain\` - Le Coeur Métier Pur +- **Dépendance** : Ne dépend de **RIEN** (sauf `Shared\`). **Aucun import Laravel / Eloquent / HTTP n'est toléré**. +- **Contenu** : + - **`Models/Entities/`** : Entités riches, `final readonly`, immutables, implémentant `Shared\DDD\Model\Entity`. + - **`Models/ValueObjects/`** : Objets-valeurs typés et auto-validants (ex: `ArcadiaEmployeeId`, `NutriScore`, `BrandCode`). + - **`Ports/`** : Interfaces des opérations externes : + - `Directories/` : Ports de lecture seule (pattern `Directory`). + - `Repositories/` : Ports de persistance / écriture (`Repository`). + - `Services/` : Interfaces pour services externes métier. + - **`Events/`** : Événements métier purs (ex: `BrandCreated`). + - **`Services/`** : Logique métier pure impliquant plusieurs entités. + +--- + +### 2. `Application\` - L'Orchestration des Cas d'Usage +- **Dépendance** : Dépend uniquement de `Domain\` et `Shared\`. Pas de Laravel ni d'HTTP. +- **Contenu** : Cas d'usage métier découpés par contexte (`Application/Arcadia/Usecase/{Context}/{Action}/`). + - Chaque cas d'usage se compose d'un triplet obligatoire : + - `Input.php` : DTO d'entrée typé (`Shared\DDD\Usecase\Input`). + - `Output.php` : DTO de sortie agnostique (`Shared\DDD\Usecase\Output`). + - `{ActionName}.php` : Classe orchestratrice (`Shared\DDD\Usecase\Usecase`). + +--- + +### 3. `Presentation\` - Les Points d'Entrée Utilisateur / API +- **Dépendance** : Dépend de `Application\`, `Domain\` et `Shared\`. Peut utiliser les composants HTTP de Laravel. +- **Contenu** : + - `Controllers/` : Single-action invokables (`__invoke`). Reçoivent la `FormRequest`, construisent l'`Input`, appellent `UsecaseBuilder` et retournent une `JsonResponse`. + - `Requests/` : `FormRequest` pour la validation syntaxique HTTP. + - `Presenters/` & `Views/` : Transformateurs de l'`Output` Use Case en DTO de vue spécifique (`Presenter`). + - `Responses/` & `Dto/` : Sérialisation finale en JSON (conformité OpenAPI). + +--- + +### 4. `Infra\` - L'Infrastructure et les Adaptateurs Techniques +- **Dépendance** : Dépend de `Domain\`, `Application\` et `Shared\`. +- **Règle d'or** : **Aucune autre couche n'importe `Infra\`**. C'est le conteneur d'injection de dépendances (Service Provider) qui lie les Ports du Domaine aux Adaptateurs d'Infra. +- **Contenu** : + - `Adapters/` : Implémentations Eloquent des Ports (`EloquentEmployeeDirectory`, `EloquentBrandRepository`). + - `Database/Models/` : Modèles Eloquent anémiques (mapping BDD uniquement). + - `Http/Middleware/` : Middlewares techniques (HMAC, Auth). + - `Providers/` : Configuration des bindings Laravel (`DomainServiceProvider`, `SecurityServiceProvider`). + - `Usecase/LaravelExecutor.php` : Exécuteur transactionnel et logging PSR-3. + +--- + +### 5. `Shared\` - Le Socle DDD Transverse +- **Dépendance** : Zéro dépendance externe. +- **Contenu** : Interfaces de base (`Entity`, `EntityId`, `Usecase`, `Input`, `Output`, `Presenter`, `DomainEventDispatcher`, `TransactionalDomainEventDispatcher`, `UsecaseBuilder`, `Paginated`, `Pagination`). + +--- + +## 🚫 Règles de Dépendance (Vérifiées par Pest Arch) + +| Couche | Peut importer... | Ne peut JAMAIS importer... | +| :----------------- | :-------------------------------- | :---------------------------------------------------------------- | +| **`Domain`** | `Shared` | `Application`, `Presentation`, `Infra`, `Illuminate\*`, `Laravel` | +| **`Application`** | `Domain`, `Shared` | `Presentation`, `Infra`, `Illuminate\*`, `Laravel` | +| **`Presentation`** | `Application`, `Domain`, `Shared` | `Infra` *(sauf bindings conteneur)* | +| **`Infra`** | `Domain`, `Application`, `Shared` | `Presentation` | +| **`Shared`** | *Rien* | `Domain`, `Application`, `Presentation`, `Infra`, `Illuminate\*` | diff --git a/documentation/Architecture/02-cycle-de-vie-requete.md b/documentation/Architecture/02-cycle-de-vie-requete.md new file mode 100644 index 0000000..1eafae8 --- /dev/null +++ b/documentation/Architecture/02-cycle-de-vie-requete.md @@ -0,0 +1,164 @@ +# Cycle de Vie d'une Requête (Bullet Tracer) - Arcadia API + +Ce document détaille le cheminement complet d'une requête HTTP à travers toutes les couches de l'application, étape par étape. + +--- + +## 🗺️ Vue d'Ensemble du Flux + +```mermaid +flowchart TD + Client(["Client HTTP"]) -->|"GET /api/v1/employees/12345678"| Route["routes/apiv1.php"] + Route -->|"Vérifie HMAC & Nonce"| HMAC["VerifyHmacSignature (Middleware)"] + HMAC -->|"Requête valide"| Request["EmployeeShowRequest (Validation HTTP)"] + Request --> Controller["EmployeeShowController (__invoke)"] + + subgraph Presentation Couche + Controller -->|"Instancie"| Input["Input(ArcadiaEmployeeId)"] + Controller -->|"Lance"| Builder["UsecaseBuilder::using(executor)"] + end + + subgraph Infra Execution + Builder -->|"Englobe transaction & log"| Executor["LaravelExecutor"] + end + + subgraph Application & Domain + Executor --> Usecase["FindEmployee::execute(input)"] + Usecase --> DirectoryPort["EmployeeDirectory (Port Domain)"] + DirectoryPort -.->|"Implémenté par"| EloquentDir["EloquentEmployeeDirectory (Adapter Infra)"] + EloquentDir --> DB[("Base de Données")] + EloquentDir -->|"Hydrate"| Entity["Employee (Entité Domain)"] + Usecase -->|"Retourne"| Output["Output(Employee)"] + end + + subgraph Presentation Formatting + Executor -->|"Applique"| Presenter["EmployeeShowPresenter::present(output)"] + Presenter --> View["EmployeeShowView"] + Controller --> Response["EmployeeShowResponse($view)"] + Response --> DTO["EmployeeDto::fromView($view)"] + end + + Response -->|"HTTP 200 JSON"| Client +``` + +--- + +## 🔍 Les 7 Étapes Détaillées + +### Étape 1 : Routage (`routes/apiv1.php`) +La requête arrive sur l'endpoint configuré dans `routes/apiv1.php` avec le préfixe `/api/v1/` : +```php +Route::get('/employees/{identifier}', EmployeeShowController::class) + ->name('employees.show'); +``` + +--- + +### Étape 2 : Sécurité & Signature HMAC (`VerifyHmacSignature`) +Le middleware de sécurité intercepte la requête : +1. Valide la présence des 4 en-têtes : `X-Client-Identifier`, `X-Timestamp`, `X-Nonce`, `X-Signature`. +2. Vérifie que le timestamp est dans la fenêtre de tolérance (+/-5 minutes). +3. Vérifie l'unicité du nonce (`CachedNonceValidator`) pour bloquer toute attaque par rejeu. +4. Recalcule la signature HMAC-SHA256 (`METHOD|URI|TIMESTAMP|NONCE|BODY_HASH`) et la compare. +5. Injecte les `ApiClientUserDetails` dans les attributs de la requête. + +--- + +### Étape 3 : Validation HTTP & Contrôleur (`Presentation\Api\V1\`) +Le contrôleur est **invokable** et à action unique (Single Action Controller) : +1. La `FormRequest` (`EmployeeShowRequest`) valide les types et formats HTTP (ex: regex de l'identifiant). +2. Le contrôleur extrait les paramètres et instancie l'`Input` du Use Case : +```php +final readonly class EmployeeShowController +{ + public function __construct( + private FindEmployee $usecase, + private LaravelExecutor $executor, + ) {} + + public function __invoke(EmployeeShowRequest $request, string $identifier): JsonResponse + { + $input = new Input(new ArcadiaEmployeeId($identifier)); + + /** @var EmployeeShowView $view */ + $view = UsecaseBuilder::using($this->executor) + ->do($this->usecase) + ->from($input) + ->with(new EmployeeShowPresenter()) + ->run(); + + return (new EmployeeShowResponse($view))->present(); + } +} +``` + +--- + +### Étape 4 : Exécution Orchestrée (`LaravelExecutor`) +Le `UsecaseBuilder` délègue l'exécution au `LaravelExecutor` : +1. Enregistre un log PSR-3 `debug` avec le contexte complet (Use Case, Input, Utilisateur connecté). +2. Si le Use Case implémente `Transactional`, il est enveloppé dans `$database->transaction(...)`. +3. Exécute la méthode `$usecase->execute($input)`. +4. Après succès, libère automatiquement les événements de domaine (`$eventDispatcher->release()`). +5. En cas d'exception, logue l'erreur, purge les événements non commités (`$eventDispatcher->clear()`) et propage l'exception vers le handler global d'API. + +--- + +### Étape 5 : Logique Métier & Ports (`Application\` & `Domain\`) +Le Use Case orchestre l'accès aux données via les interfaces de ports du Domaine : +```php +final readonly class FindEmployee implements Usecase +{ + public function __construct( + private EmployeeDirectory $directory // Port Domain (Interface) + ) {} + + public function execute(BaseInput $input): Output + { + assert($input instanceof Input); + + $employee = $this->directory->find($input->id); + if ($employee === null) { + throw new EmployeeNotFoundException($input->id); + } + + return new Output($employee); + } +} +``` +- L'adaptateur d'Infrastructure (`EloquentEmployeeDirectory`) exécute la requête Eloquent/SQL et hydrate une entité pure du domaine (`Employee`) via son constructeur typé ou un Builder. + +--- + +### Étape 6 : Présentation & Transformation (`Presenter` -> `View`) +L'`Output` agnostique retourné par le Use Case est confié au `Presenter` : +```php +final readonly class EmployeeShowPresenter implements Presenter +{ + public function present(UsecaseOutput $output): EmployeeShowView + { + assert($output instanceof Output); + return EmployeeShowView::fromOutput($output); + } +} +``` +- Le `EmployeeShowView` isole la structure d'affichage attendue par l'API sans polluer la couche Application. + +--- + +### Étape 7 : Sérialisation DTO & Réponse JSON (`Response` -> `Dto`) +La classe Response formate la vue en JSON conforme à la spécification OpenAPI : +```php +final readonly class EmployeeShowResponse implements ApiResponse +{ + public function __construct(private EmployeeShowView $view) {} + + public function present(): JsonResponse + { + return response()->json([ + 'data' => (new EmployeeDto($this->view->employee))->toArray(), + ], 200); + } +} +``` +- Les DTOs (`Presentation\Api\V1\Dto\`) garantissent la conversion exacte des Value Objects en types scalaires primitifs (`string`, `int`, `bool`, `ISO-8601`). diff --git a/documentation/Architecture/03-execution-et-transactions.md b/documentation/Architecture/03-execution-et-transactions.md new file mode 100644 index 0000000..c477492 --- /dev/null +++ b/documentation/Architecture/03-execution-et-transactions.md @@ -0,0 +1,125 @@ +# Exécution, Transactions & Événements de Domaine - Arcadia API + +Ce document détaille le fonctionnement de l'exécuteur de cas d'usage (`LaravelExecutor`), de la gestion des transactions SQL et du cycle de vie des **Événements de Domaine (Domain Events)**. + +--- + +## ⚡ 1. Le Fluent Builder (`UsecaseBuilder`) + +Pour garantir un typage strict et une exécution homogène à travers toute l'application, les Use Cases sont exécutés via un builder fluent : + +```php +$result = UsecaseBuilder::using($this->executor) + ->do($this->usecase) // Le Use Case à exécuter + ->from($input) // Le DTO Input entrant + ->with(new CustomPresenter()) // (Optionnel) Le Presenter + ->run(); // Exécute et retourne le View (ou l'Output) +``` + +Le builder garantit à la compilation (PHPStan Niveau 10) que les types d'`Input`, d'`Output` et de `View` concordent parfaitement. + +--- + +## 🛡️ 2. Gestion des Transactions (`Transactional`) + +Pour distinguer clairement les opérations de **lecture (Query)** des opérations d'**écriture (Command)** : + +- **Cas d'Usage en Lecture (Query)** : N'implémente pas `Transactional`. Exécuté directement sans overhead de transaction de base de données. +- **Cas d'Usage en Écriture (Command)** : Implémente le marqueur `Shared\DDD\Usecase\Transactional`. + +```php +namespace Application\Security\Usecases\CreateClient; + +use Shared\DDD\Usecase\Transactional; +use Shared\DDD\Usecase\Usecase; + +/** @implements Usecase */ +final readonly class CreateClient implements Transactional, Usecase +{ + public function execute(BaseInput $input): Output + { + // Cette méthode sera automatiquement enveloppée dans + // DB::transaction(...) par le LaravelExecutor + } +} +``` + +--- + +## 📢 3. Événements de Domaine & Sécurité Post-Commit + +Dans les architectures événementielles, un problème classique survient lorsqu'un événement est émis **pendant** une transaction SQL qui échoue ensuite (rollback) : les écouteurs ont déjà envoyé des notifications ou loggué des actions erronées. + +Arcadia API résout ce problème via le pattern **`TransactionalDomainEventDispatcher`** : + +```mermaid +sequenceDiagram + autonumber + participant UseCase as Cas d'Usage + participant Dispatcher as TransactionalDomainEventDispatcher (Buffer) + participant DB as Base de Données + participant Executor as LaravelExecutor + participant LaravelBus as Laravel Event Bus + + Executor->>DB: DB::beginTransaction() + Executor->>UseCase: execute(input) + UseCase->>Dispatcher: dispatch(BrandCreated) + Note over Dispatcher: Événement stocké dans le buffer mémoire (non émis) + UseCase-->>Executor: Output + + alt Succès de la Transaction + Executor->>DB: DB::commit() + Executor->>Dispatcher: release() + Dispatcher->>LaravelBus: dispatch(BrandCreated) + Note over LaravelBus: Les Listeners sont déclenchés après le commit garanti + else Exception / Rollback + Executor->>DB: DB::rollBack() + Executor->>Dispatcher: clear() + Note over Dispatcher: Le buffer est vidé, aucun événement fantôme n'est émis + end +``` + +--- + +## 💻 4. Comment Émettre un Événement dans le Domaine ? + +Dans un service ou un Use Case de domaine, injectez simplement `Shared\DDD\Event\DomainEventDispatcher` : + +```php +namespace Domain\Services; + +use Domain\Events\BrandCreated; +use Shared\DDD\Event\DomainEventDispatcher; + +final readonly class BrandResolver +{ + public function __construct( + private BrandRepository $brands, + private DomainEventDispatcher $dispatcher, + ) {} + + public function resolve(string $brandName): Brand + { + // Logique de résolution / création... + $brand = Brand::create(...); + $this->brands->persist($brand); + + // Déclencher l'événement métier + $this->dispatcher->dispatch(new BrandCreated($brand)); + + return $brand; + } +} +``` + +### Enregistrer un Écouteur (Listener) +Dans `Infra\Providers\EventServiceProvider.php` : +```php +use Domain\Events\BrandCreated; +use Infra\Listeners\LogBrandCreated; + +Event::listen( + BrandCreated::class, + LogBrandCreated::class, +); +``` diff --git a/documentation/Architecture/04-guide-nouveau-usecase.md b/documentation/Architecture/04-guide-nouveau-usecase.md new file mode 100644 index 0000000..6f3a27b --- /dev/null +++ b/documentation/Architecture/04-guide-nouveau-usecase.md @@ -0,0 +1,144 @@ +# Guide Pratique : Créer un Nouveau Cas d'Usage - Arcadia API + +Ce guide pas-à-pas accompagne un développeur dans la création d'un nouveau cas d'usage complet (ex: `GET /api/v1/articles/{id}`). + +--- + +## 📋 Checklist en 5 Étapes + +1. **Domaine (`Domain\`)** : Définir l'Entité, les Value Objects et le Port (Interface). +2. **Infrastructure (`Infra\`)** : Implémenter l'adaptateur Eloquent et enregistrer le binding. +3. **Application (`Application\`)** : Créer le triplet `Input`, `Output` et `ActionName` (Use Case). +4. **Présentation (`Presentation\`)** : Créer `Presenter`, `View`, `Controller`, `Request` et `Response`. +5. **Tests (`tests/`)** : Rédiger les tests Unitaires, d'Intégration et de Feature (OpenAPI). + +--- + +## 🛠️ Étape par Étape + +### 1. Domaine : Le Port & l'Entité +Définissez l'interface dans `src/Domain/Ports/Directories/ArticleDirectory.php` : +```php +namespace Domain\Ports\Directories; + +use Domain\Models\Entities\Article; +use Domain\Models\ValueObjects\ArticleId; +use Shared\DDD\Port\Directory; + +/** @extends Directory */ +interface ArticleDirectory extends Directory +{ + public function find(ArticleId $id): ?Article; +} +``` + +--- + +### 2. Infrastructure : L'Adaptateur Eloquent +Implémentez l'interface dans `src/Infra/Adapters/Directories/EloquentArticleDirectory.php` : +```php +namespace Infra\Adapters\Directories; + +use Domain\Models\Entities\Article; +use Domain\Models\ValueObjects\ArticleId; +use Domain\Ports\Directories\ArticleDirectory; +use Infra\Database\Models\Article as EloquentArticle; + +final class EloquentArticleDirectory implements ArticleDirectory +{ + #[\Override] + public function find(ArticleId $id): ?Article + { + $model = EloquentArticle::query()->find($id->value); + return $model ? $this->toDomain($model) : null; + } + + private function toDomain(EloquentArticle $model): Article + { + return Article::hydrate(...); + } +} +``` +Enregistrez le binding dans `src/Infra/Providers/DomainServiceProvider.php` : +```php +$this->app->bind(ArticleDirectory::class, EloquentArticleDirectory::class); +``` + +--- + +### 3. Application : Le Use Case +Dans `src/Application/Arcadia/Usecase/Articles/Find/` : + +**`Input.php`** : +```php +namespace Application\Arcadia\Usecase\Articles\Find; + +use Domain\Models\ValueObjects\ArticleId; +use Shared\DDD\Usecase\Input as BaseInput; + +final readonly class Input implements BaseInput +{ + public function __construct(public ArticleId $id) {} +} +``` + +**`Output.php`** : +```php +namespace Application\Arcadia\Usecase\Articles\Find; + +use Domain\Models\Entities\Article; +use Shared\DDD\Usecase\Output as BaseOutput; + +final readonly class Output implements BaseOutput +{ + public function __construct(public Article $article) {} +} +``` + +**`FindArticle.php`** : +```php +namespace Application\Arcadia\Usecase\Articles\Find; + +use Application\Arcadia\Exceptions\ArticleNotFoundException; +use Domain\Ports\Directories\ArticleDirectory; +use Shared\DDD\Usecase\Input as BaseInput; +use Shared\DDD\Usecase\Usecase; + +/** @implements Usecase */ +final readonly class FindArticle implements Usecase +{ + public function __construct(private ArticleDirectory $directory) {} + + public function execute(BaseInput $input): Output + { + assert($input instanceof Input); + + $article = $this->directory->find($input->id); + if ($article === null) { + throw new ArticleNotFoundException($input->id); + } + + return new Output($article); + } +} +``` + +--- + +### 4. Présentation : Contrôleur & Presenter +1. **Presenter** (`ArticleShowPresenter`) : convertit `Output` en `ArticleShowView`. +2. **Controller** (`ArticleShowController`) : Single-action invokable exécutant `UsecaseBuilder`. +3. **Response** (`ArticleShowResponse`) : formate la vue en JSON avec `ArticleDto`. +4. **Route** dans `routes/apiv1.php` : +```php +Route::get('/articles/{identifier}', ArticleShowController::class) + ->name('articles.show'); +``` + +--- + +### 5. Tests +1. **Unit Test** (`tests/Unit/Application/Arcadia/Usecase/Articles/FindArticleTest.php`) : avec `InMemoryArticleDirectory` et `Object Mother`. +2. **Integration Test** (`tests/Integration/Infra/Adapters/Directories/EloquentArticleDirectoryTest.php`) : test de la requête réelle sur base SQLite/MySQL de test. +3. **Feature Test** (`tests/Feature/Api/V1/Articles/ArticleShowTest.php`) : test HTTP de bout en bout avec signature HMAC. +4. **OpenAPI Test** (`tests/Feature/OpenApi/V1/Articles/ArticleOpenApiTest.php`) : conformité stricte au schéma `openapi.yaml`. diff --git a/documentation/Conventions/exceptions.md b/documentation/Conventions/exceptions.md new file mode 100644 index 0000000..4a87c62 --- /dev/null +++ b/documentation/Conventions/exceptions.md @@ -0,0 +1,62 @@ +# Gestion des Exceptions & Codes HTTP - Arcadia API + +Ce document récapitule la stratégie de gestion des exceptions et la correspondance avec les codes de statut HTTP dans **Arcadia API**. + +--- + +## 📑 Sommaire + +- [1. Convention REST & RFC 7807](#1-convention-rest--rfc-7807) +- [2. Matrice de Mapping Exceptions -> Codes HTTP](#2-matrice-de-mapping-exceptions--codes-http) +- [3. Format de Réponse (Problem Details)](#3-format-de-réponse-problem-details) +- [4. Distinctions 400 Bad Request vs 422 Unprocessable Content](#4-distinctions-400-bad-request-vs-422-unprocessable-content) + +--- + +## 1. Convention REST & RFC 7807 + +L'API exprime toutes les réponses d'erreur au format standardisé **RFC 7807 / RFC 9457 Problem Details** avec l'en-tête HTTP : +`Content-Type: application/problem+json` + +--- + +## 2. Matrice de Mapping Exceptions -> Codes HTTP + +| Type d'Exception | Couche Origine | Code HTTP | Titre RFC 7807 | Usage & Description | +| :----------------------------- | :----------------------- | :-------: | :---------------------- | :------------------------------------------------------------------------------------- | +| `ValidationException` | `Presentation` | **422** | `Unprocessable Entity` | Échec de validation HTTP (`FormRequest`), champs invalides ou manquants. | +| `DomainException` | `Domain` | **422** | `Unprocessable Entity` | Violation d'un invariant ou d'une règle métier au cœur du Domaine (ex: `ValueObject`). | +| `ConflictException` | `Domain` / `Application` | **409** | `Conflict` | Conflit d'état métier (ex: ressource déjà existante ou verrouillée). | +| `NotFoundException` | `Application` | **404** | `Resource Not Found` | Ressource introuvable par le Use Case ou le répertoire. | +| `HttpExceptionInterface` (403) | `Presentation` | **403** | `Forbidden` | Accès refusé par les politiques d'autorisation HTTP. | +| `ThrottleRequestsException` | `Infra` | **429** | `Too Many Requests` | Dépassement du quota de requêtes API (Rate Limiting). | +| `Throwable` / `Exception` | Transverse | **500** | `Internal Server Error` | Erreur système non captée (détails masqués hors mode debug). | + +--- + +## 3. Format de Réponse (Problem Details) + +Chaque réponse d'erreur retourne une structure JSON standardisée : + +```json +{ + "type": "http://localhost/errors/422", + "title": "Unprocessable Entity", + "status": 422, + "detail": "Invalid employee number format", + "instance": "/api/v1/employees", + "invalidParams": [ + { + "name": "domain_invariant", + "reason": "Invalid employee number format" + } + ] +} +``` + +--- + +## 4. Distinctions 400 Bad Request vs 422 Unprocessable Content + +- **HTTP 400 (Bad Request)** : Réservé aux erreurs de **syntaxe ou de structure HTTP** (ex: JSON illisible, guillemets mal fermés, en-têtes HMAC malformés). Le serveur ne peut pas parser le message. +- **HTTP 422 (Unprocessable Content)** : Convention **REST & Laravel**. La requête est du JSON syntaxiquement valide, mais son contenu viole les règles de validation HTTP (`FormRequest`) ou les invariants du domaine métier (`DomainException`). diff --git a/documentation/Conventions/tests.md b/documentation/Conventions/tests.md new file mode 100644 index 0000000..37a6ee0 --- /dev/null +++ b/documentation/Conventions/tests.md @@ -0,0 +1,120 @@ +# Conventions de Tests - Arcadia API + +Ce document récapitule la stratégie, l'organisation et les conventions strictes applicables à la suite de tests dans **Arcadia API**. + +--- + +## 🚀 1. Commandes + +```bash +# Exécuter l'ensemble de la suite de tests (Pest 4) +composer test + +# Exécuter les tests avec couverture de code (seuil minimal 85%) +composer test:coverage + +# Exécuter uniquement les tests d'architecture +vendor/bin/pest tests/Architecture +``` + +--- + +## 🏔️ 2. Pyramide de Tests + +``` + . + / \ + / \ 1. FEATURE TESTS (Contrat OpenAPI & E2E) + / \ ---------------------------------------- + / Feature\ - Respect du contrat OpenAPI (openapi.yaml) + /----------\ - Codes statut HTTP (200, 404, 422, 429) & Auth HMAC + / \ - Validation de bout en bout des réponses JSON + / Integration \ + /----------------\ 2. INTEGRATION TESTS (Infra & Laravel) + / \ ---------------------------------------- + / \- Adaptateurs Eloquent (requêtes BDD réelles) + / Unit \- Service Providers & Bindings Laravel + / \- Exécuteur transactionnel & Dispatcher d'événements + /--------------------------\ + / \ 3. UNIT TESTS (Domaine & Use Cases) + / \------------------------------------ + / \- Entités DDD, Value Objects, règles métier + / \- Use Cases (avec InMemory Directories & Mocks) +/____________________________________\- Pure PHP (sans conteneur Laravel, instantané) +``` + +--- + +## 📁 3. Structure des Dossiers + +L'arborescence `tests/` reproduit en miroir la structure de `src/` : + +- `tests/Architecture/` : Tests de conformité d'architecture DDD, dépendances inter-couches et conventions (Pest Arch). +- `tests/Unit/` : Tests unitaires purs (sans conteneur Laravel, sans BDD, exécution instantanée). +- `tests/Integration/` : Tests des adaptateurs d'infrastructure (Eloquent, événements, base SQLite de test). +- `tests/Feature/` : Tests de bout en bout des endpoints API et validation OpenAPI (`openapi.yaml`). +- **`tests/Support/`** : Utilitaires de test partagés (**Test Data Builders & Object Mothers**). + +--- + +## 💎 4. Conventions de Code + +### Principes F.I.R.S.T. +Chaque test rédigé dans ce projet doit obligatoirement respecter les principes **F.I.R.S.T.** : + +- **Fast** (Rapide) : Les tests unitaires doivent s'exécuter en quelques millisecondes. +- **Isolated / Independent** (Isolé) : Les tests ne doivent jamais dépendre les uns des autres ni de l'ordre d'exécution. +- **Repeatable** (Répétable) : Le test produit le même résultat sur toutes les machines et en CI. +- **Self-validating** (Auto-validant) : Le résultat est binaire (`OK` / `FAIL`), sans vérification manuelle. +- **Timely** (Opportun) : Les tests sont écrits au bon moment (en TDD ou simultanément avec le code). + +### Déclaration `covers()` et Structure GWT +Tous les tests unitaires et d'intégration doivent obligatoirement déclarer la classe ciblée via `covers(NomDeLaClasse::class);` et structurer leur logique avec les blocs `// GIVEN`, `// WHEN`, `// THEN` : + +```php +namespace Tests\Unit\Presentation\Api\V1\Presenters\Employees; + +use Presentation\Api\V1\Presenters\Employees\EmployeeShowPresenter; +use Presentation\Api\V1\Presenters\Employees\EmployeeShowView; +use Tests\Support\Mothers\EmployeeMother; + +covers(EmployeeShowPresenter::class, EmployeeShowView::class); + +it('transforms Output into EmployeeShowView', function (): void { + // GIVEN + $employee = EmployeeMother::create('12345678', 'Dupont', 'Jean'); + $output = new Output($employee); + $presenter = new EmployeeShowPresenter(); + + // WHEN + $view = $presenter->present($output); + + // THEN + expect($view)->toBeInstanceOf(EmployeeShowView::class) + ->and($view->employee)->toBe($employee); +}); +``` + +--- + +## 🏗️ 5. Test Data Builders & Object Mothers (`tests/Support/`) + +Pour éviter la fragilité des tests face aux modifications de constructeurs complexes du Domaine (ex: `IncoComposition` et ses 16 Value Objects), **utilisez toujours les Test Data Builders ou Object Mothers** : + +### Object Mothers (`tests/Support/Mothers/`) +Fournissent des instances prêtes à l'emploi et nommées pour les scénarios standards : +```php +// Instancie un objet valide avec des valeurs par défaut réalistes +$composition = IncoCompositionMother::create(); +$incoSheet = IncoSheetMother::pouletRoti(); +$employee = EmployeeMother::create('12345678'); +``` + +### Test Data Builders (`tests/Support/Builders/`) +Permettent de surcharger précisément les propriétés pertinentes pour le test sans se soucier du reste du graphe : +```php +$composition = IncoCompositionTestBuilder::create() + ->withId('67890') + ->withNutriScore('A') + ->build(); +``` diff --git a/documentation/Exceptions.md b/documentation/Exceptions.md deleted file mode 100644 index 913d675..0000000 --- a/documentation/Exceptions.md +++ /dev/null @@ -1,60 +0,0 @@ -# Gestion des Exceptions & Codes HTTP — Arcadia API - -Ce document récapitule la stratégie de gestion des exceptions et la correspondance avec les codes de statut HTTP dans **Arcadia API**. - -## Sommaire - -- [1. Convention REST & RFC 7807](#1-convention-rest--rfc-7807) -- [2. Matrice de Mapping Exceptions ➔ Codes HTTP](#2-matrice-de-mapping-exceptions--codes-http) -- [3. Format de Réponse (Problem Details)](#3-format-de-réponse-problem-details) -- [4. Distinctions 400 Bad Request vs 422 Unprocessable Content](#4-distinctions-400-bad-request-vs-422-unprocessable-content) - ---- - -## 1. Convention REST & RFC 7807 - -L'API exprime toutes les réponses d'erreur au format **RFC 7807 / RFC 9457 Problem Details** avec l'en-tête HTTP : -`Content-Type: application/problem+json` - ---- - -## 2. Matrice de Mapping Exceptions ➔ Codes HTTP - -| Type d'Exception | Couche Origine | Code HTTP | Titre RFC 7807 | Usage & Description | -| ------------------------------ | ------------------------ | --------- | ----------------------- | --------------------------------------------------------------------------------------- | -| `ValidationException` | `Presentation` | **422** | `Unprocessable Entity` | Échec de validation HTTP (`FormRequest`), champs invalides ou manquants. | -| `DomainException` | `Domain` | **422** | `Unprocessable Entity` | Violation d'un invariant ou d'une règle métier au coeur du Domaine (ex: `ValueObject`). | -| `ConflictException` | `Domain` / `Application` | **409** | `Conflict` | Conflit d'état métier (ex: compte déjà supprimé, ressource déjà existante/verrouillée). | -| `NotFoundException` | `Application` | **404** | `Resource Not Found` | Ressource introuvable par le Use Case / répertoire. | -| `HttpExceptionInterface` (403) | `Presentation` | **403** | `Forbidden` | Accès refusé par les politiques d'autorisation HTTP. | -| `ThrottleRequestsException` | `Infra` | **429** | `Too Many Requests` | Dépassement du quota de requêtes API (Rate Limiting). | -| `Throwable` / `Exception` | Transverse | **500** | `Internal Server Error` | Erreur système non captée (détails masqués hors mode debug). | - ---- - -## 3. Format de Réponse (Problem Details) - -Chaque réponse d'erreur retourne une structure JSON standardisée : - -```json -{ - "type": "http://localhost/errors/422", - "title": "Unprocessable Entity", - "status": 422, - "detail": "Invalid employee number format", - "instance": "/api/v1/employees", - "invalidParams": [ - { - "name": "domain_invariant", - "reason": "Invalid employee number format" - } - ] -} -``` - ---- - -## 4. Distinctions 400 Bad Request vs 422 Unprocessable Content - -- **HTTP 400 (Bad Request)** : Réservé aux erreurs de **syntaxe ou de structure HTTP** (ex: JSON illisible, guillemets mal fermés, en-têtes HMAC malformés). Le serveur ne peut pas parser le message. -- **HTTP 422 (Unprocessable Content)** : Convention **REST & Laravel**. La requête est du JSON syntaxiquement valide, mais son contenu viole les règles de validation HTTP (`FormRequest`) ou les invariants du domaine métier (`DomainException`). diff --git a/documentation/OpenApi/openapi.yaml b/documentation/OpenApi/openapi.yaml index 5bed008..e9130ca 100644 --- a/documentation/OpenApi/openapi.yaml +++ b/documentation/OpenApi/openapi.yaml @@ -337,7 +337,7 @@ components: type: apiKey in: header name: X-Timestamp - description: Horodatage au format RFC3339 (tolérance anti-replay ±5 minutes). + description: Horodatage au format RFC3339 (tolérance anti-replay +/-5 minutes). HmacNonce: type: apiKey in: header diff --git a/documentation/Tests.md b/documentation/Tests.md deleted file mode 100644 index 35ef0db..0000000 --- a/documentation/Tests.md +++ /dev/null @@ -1,94 +0,0 @@ -# Tests — Arcadia API - -Ce document récapitule la stratégie et l'organisation des tests dans **Arcadia API**. - -## Sommaire - -- [Tests — Arcadia API](#tests--arcadia-api) - - [Sommaire](#sommaire) - - [1. Commandes](#1-commandes) - - [2. Pyramide de Tests](#2-pyramide-de-tests) - - [3. Structure des Dossiers](#3-structure-des-dossiers) - - [4. Conventions de Code](#4-conventions-de-code) - - [Principes F.I.R.S.T.](#principes-first) - - [Pattern GWT (Given - When - Then)](#pattern-gwt-given---when---then) - ---- - -## 1. Commandes - -```bash -# Exécuter l'ensemble de la suite de tests -composer test - -# Exécuter les tests avec couverture de code (seuil minimal 85%) -composer test:coverage -``` - -## 2. Pyramide de Tests - -``` - . - / \ - / \ 1. FEATURE TESTS (Contrat OpenAPI & E2E) - / \ ---------------------------------------- - / Feature\ - Respect du contrat OpenAPI (openapi.yaml) - /----------\ - Codes statut HTTP (200, 404, 422, 429) & Auth HMAC - / \ - Validation de bout en bout des réponses JSON - / Integration \ - /----------------\ 2. INTEGRATION TESTS (Infra & Laravel) - / \ ---------------------------------------- - / \- Adaptateurs Eloquent (requêtes BDD réelles) - / Unit \- Service Providers & Bindings Laravel - / \- Clients & adaptateurs de services externes - /--------------------------\ - / \ 3. UNIT TESTS (Domaine & Use Cases) - / \------------------------------------ - / \- Entités DDD, Value Objects, règles métier - / \- Use Cases (avec Mocks de Ports) -/____________________________________\- Pure PHP (sans instanciation de Laravel) -``` - -## 3. Structure des Dossiers - -L'arborescence `tests/` reproduit en miroir la structure de `src/` : - -- `tests/Architecture/` : Tests de règles d'architecture DDD, dépendances inter-couches et conventions (Pest Arch). -- `tests/Unit/` : Tests unitaires purs (sans conteneur Laravel, sans BDD, exécution instantanée). -- `tests/Integration/` : Tests des adaptateurs d'infrastructure (BDD, services). -- `tests/Feature/` : Tests de bout en bout et de conformité des endpoints API : - - `tests/Feature/Api/V1/{Context}/` : Tests fonctionnels des endpoints HTTP et de leurs query parameters. - - `tests/Feature/OpenApi/V1/{Context}/` : Tests de conformité au contrat OpenAPI (`openapi.yaml`). - -## 4. Conventions de Code - -### Principes F.I.R.S.T. -Chaque test rédigé dans ce projet doit obligatoirement respecter les principes **F.I.R.S.T.** : - -- **Fast** (Rapide) : Les tests (en particulier unitaires) doivent s'exécuter en quelques millisecondes pour un feedback immédiat. -- **Isolated / Independent** (Isolé) : Les tests ne doivent jamais dépendre les uns des autres ni de l'ordre d'exécution. Chaque test initialise son propre état et le nettoie. -- **Repeatable** (Répétable) : Le test produit exactement le même résultat dans tous les environnements (machine locale, CI GitLab, conteneur Docker). -- **Self-validating** (Auto-validant) : Le test passe ou échoue de façon binaire (`OK` / `FAIL`), sans nécessiter d'analyse manuelle des logs. -- **Timely** (Opportun) : Les tests sont écrits au bon moment (en TDD ou simultanément avec le code de production). - -### Declaration covers() et Pattern GWT -Tous les tests unitaires (`tests/Unit`) et d'intégration (`tests/Integration`) doivent obligatoirement déclarer la classe ciblée via `covers(NomDeLaClasse::class);`, utiliser des blocs de commentaires explicites `// GIVEN`, `// WHEN`, `// THEN` et appartenir à un namespace `Tests\...` (vérifié par test d'architecture) : - -```php -namespace Tests\Unit\Domain\Models\ValueObjects; - -use Domain\Models\ValueObjects\Entities\ArcadiaEmployeeId; - -covers(ArcadiaEmployeeId::class); - -it('validates an employee id', function (): void { - // GIVEN - $id = '12345678'; - - // WHEN - $entityId = new ArcadiaEmployeeId($id); - - // THEN - expect($entityId->value)->toBe($id); -}); -```