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
This commit is contained in:
parent
20ae9a2307
commit
0d2b0124ee
11 changed files with 943 additions and 388 deletions
51
README.md
51
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
|
||||
|
||||

|
||||
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
|
||||
```
|
||||
|
|
|
|||
|
|
@ -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é.
|
||||
|
|
|
|||
95
documentation/Architecture/01-architecture-globale.md
Normal file
95
documentation/Architecture/01-architecture-globale.md
Normal file
|
|
@ -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<TId, TEntity>`).
|
||||
- `Repositories/` : Ports de persistance / écriture (`Repository<TId, TEntity>`).
|
||||
- `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<Input, Output>`).
|
||||
|
||||
---
|
||||
|
||||
### 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<Output, View>`).
|
||||
- `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\*` |
|
||||
164
documentation/Architecture/02-cycle-de-vie-requete.md
Normal file
164
documentation/Architecture/02-cycle-de-vie-requete.md
Normal file
|
|
@ -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`).
|
||||
125
documentation/Architecture/03-execution-et-transactions.md
Normal file
125
documentation/Architecture/03-execution-et-transactions.md
Normal file
|
|
@ -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<Input, Output> */
|
||||
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,
|
||||
);
|
||||
```
|
||||
144
documentation/Architecture/04-guide-nouveau-usecase.md
Normal file
144
documentation/Architecture/04-guide-nouveau-usecase.md
Normal file
|
|
@ -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<ArticleId, Article> */
|
||||
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<Input, Output> */
|
||||
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`.
|
||||
62
documentation/Conventions/exceptions.md
Normal file
62
documentation/Conventions/exceptions.md
Normal file
|
|
@ -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`).
|
||||
120
documentation/Conventions/tests.md
Normal file
120
documentation/Conventions/tests.md
Normal file
|
|
@ -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();
|
||||
```
|
||||
|
|
@ -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`).
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
```
|
||||
Loading…
Add table
Reference in a new issue