# RETEX M1 (Clients) → à appliquer pour M2 (Fournisseurs) > But : éviter de reproduire en M2 les erreurs de **contrat de sérialisation** qui ont bloqué M1. > ~80 % des frictions M1 venaient du contrat API (sérialisation / groupes / sous-ressources), **pas** du métier. > À lire AVANT de rédiger `spec-back.md` et `spec-front.md` du M2, et à garder ouvert pendant la rédaction. --- ## 0. TL;DR (les 3 erreurs à ne jamais refaire) 1. **Affirmer qu'un champ est « embarqué » sans vérifier les 3 maillons de sérialisation.** En M1 : `Category.code` annoncé dans `client:read`, détail annoncé embarquant contacts/adresses/ribs → **faux dans le code**. Résultat : colonnes liste vides, onglets détail impossibles à peupler. 2. **Livrer des sous-ressources en POST-only** (pas de `GetCollection`, pas d'embed) → le front ne peut pas lister les enfants de l'agrégat. 3. **Écrire la spec/les tickets sur une intention, pas sur le contrat réel.** Le docblock `Client` décrivait un embed jamais implémenté. --- ## 1. Contrat de sérialisation : les 3 maillons obligatoires Pour **chaque champ affiché** (liste OU détail), la spec back doit prouver les trois maillons. Si un seul manque → le champ sort en quasi-IRI (`@id`/`@type` seulement) ou pas du tout. | Maillon | Question | Exemple M1 raté | |---|---|---| | (a) Groupe sur la **propriété** | `#[Groups([...])]` contient-il un read-group ? | `Supplier::$addresses` sans groupe → jamais sérialisé | | (b) Groupe dans le **`normalizationContext` de l'opération** | l'opération (`GetCollection`/`Get`) liste-t-elle ce groupe ? | `GetCollection` en `['client:read','default:read']` | | (c) Read-group de l'**entité imbriquée** dans le contexte parent | pour embarquer les champs d'une relation (catégorie, site…), le contexte parent inclut-il `category:read` / `site:read` ? | `Category.code` ∈ `category:read`, absent du contexte client → pas de `code` | **Règle de rédaction** : dans `spec-back.md`, faire un tableau « champ → groupe propriété → groupe(s) à ajouter au contexte de chaque opération » pour la liste ET le détail. Inclure explicitement les **relations imbriquées** (ex. catégories d'une adresse, sites d'une adresse). ## 2. Collections enfant d'un agrégat : décider embed vs GetCollection, et câbler en ENTIER Décision à acter dès la spec back pour chaque sous-collection (contacts, adresses, RIB, lignes…) : - **Embed dans le détail (recommandé pour un agrégat DDD)** : poser `#[Groups([':item:read'])]` sur la propriété + ajouter au `normalizationContext` du `Get` racine les read-groups des entités enfant **et** de leurs relations imbriquées. 1 requête, cohérent avec un composable `useX(id)`. Réservé aux ensembles **bornés** (ne viole pas la règle n°13 : elle vise les collections exposées, pas un embed borné d'item). - **GetCollection sous-ressource** : `//{id}/children` paginé. À réserver aux collections potentiellement volumineuses. Si choisi, **créer l'opération** (pas seulement POST). ❌ Anti-pattern M1 : sous-ressources avec `POST` + `Get` unitaire seulement → **aucun moyen de lister** (ids non découvrables). Interdit. ## 3. Vérifier le contrat sur l'API RÉELLE avant d'écrire les tickets front Le blocage M1 (codes/sites/sous-collections) aurait été vu en 5 min. À mettre dans la **definition of done de la spec back** : > Créer un enregistrement de test, appeler `GET /api/` (liste) ET `GET /api//{id}` (détail), **coller la réponse JSON réelle** dans la spec. Toute donnée affichée par le front doit apparaître dans ce JSON collé. ## 4. La spec décrit le RÉEL, pas l'intention - Bannir les « devrait être embarqué », « est exposé » non vérifiés. Décrire ce qui existe (ou ce qui sera livré dans le ticket, en le marquant clairement « à livrer »). - Si un docblock/commentaire existant contredit le code, le **corriger**, pas le recopier. ## 5. Réutiliser les acquis M1 (ne pas réinventer) - **Taxonomie ERP-78** : si M2 catégorise les fournisseurs, repartir du modèle **type unique + `code` stable** (slug MAJUSCULE auto-généré, NOT NULL, figé, **lecture seule** `category:read`), filtrage métier via `?categoryCode=`. Réutiliser le contrat partagé `CategoryInterface` (pas d'import inter-module). - **Front** : `usePaginatedList` (listes), composants `Malio*`, `useApi()`, `formatPhoneFR`, blocs réutilisables (Contact/Adresse), pattern de blocs dynamiques + modal de confirmation. - **Archive** : flag `is_archived` **distinct** de `deleted_at` (soft delete). Restauration → gérer le 409 homonyme. - **Normalisation = serveur** (UPPERCASE nom société, Capitalize noms, lowercase email, téléphone en chiffres). Le front envoie la saisie, réaffiche la valeur normalisée renvoyée. À documenter dans la spec. - **Gating fin + mode strict PATCH** : PATCH par groupe de sérialisation ; tout champ hors-permission dans le payload = **403 sur l'intégralité** (pas de filtrage silencieux). Spécifier la matrice rôle × onglet. ## 6. Règles ABSOLUES transverses à rappeler dans la spec M2 - **Pagination obligatoire** (règle n°13) sur toute `GetCollection` ; échappatoire `?pagination=false` réservée aux selects de référentiels bornés. - **`COMMENT ON COLUMN`** (règle n°12) sur chaque colonne créée/modifiée (sinon `make test` casse). Helper standard pour les colonnes Timestampable/Blamable. - **Timestampable + Blamable** sur toute nouvelle entité métier (4 colonnes + trait) ; garde-fou archi. - **`#[Auditable]`** sur les entités métier ; **`#[AuditIgnore]`** sur les champs sensibles (équivalents BIC/IBAN/secret). - **`declare(strict_types=1);`** partout ; commentaires FR, code EN. - **Routes front à plat** (pas de préfixe module), état tableau **jamais** dans l'URL. - **3 miroirs RBAC** à toucher ensemble : `config/sidebar.php`, `frontend/tests/e2e/_fixtures/personas.ts`, `SeedE2ECommand.php`. - **Communication inter-module** uniquement via `Shared/Domain/Contract/` ou domain events — jamais d'import direct. ## 7. Fixtures & seed dès le départ M1 a subi un aller-retour (ERP-68) faute de fixtures alignées. Pour M2 : prévoir dès la spec un seed de fournisseurs démo **couvrant tous les cas des règles métier** (relations, catégories codées, archivés, cas comptables) + comptes de rôles démo, pour vérifier le gating et le golden path sans bricolage. ## 8. Mini-checklist de relecture de la spec M2 (avant de la déclarer prête) - [ ] Chaque champ affiché (liste + détail) a ses 3 maillons de sérialisation documentés (propriété, contexte opération, relations imbriquées). - [ ] Chaque sous-collection a une décision **embed vs GetCollection** explicite et **complètement câblée** (pas de POST-only). - [ ] Réponses JSON réelles (liste + détail) collées dans la spec back. - [ ] Matrice RBAC rôle × écran × onglet + mode strict PATCH spécifiés. - [ ] Pagination, COMMENT ON COLUMN, Timestampable/Blamable, Audit, routes à plat : rappelés. - [ ] Réutilisations M1 identifiées (taxonomie code, usePaginatedList, blocs, archive, normalisation). - [ ] Seed/fixtures démo planifiés.