ENG-001.5J2 — Migration des écritures des informations municipales — Rapport d'implémentation
| Mission | ENG-001.5J2 — Migration des écritures des informations municipales |
| Rattaché à | ENG-001.5J — Migration des infos pratiques vers Municipal Management ; ENG-001.5J — Audit d'ownership des sections d'infos ; ENG-001.5J1 — Rapport d'implémentation |
| Dépôt | dmv_api |
| Branche | feature/eng-001-5j2-municipal-infos-write |
| Statut | Implémenté localement — aucun commit applicatif |
1. Divergence initiale
MunicipalManagementWriterexposait déjàcreateInfo(),updateInfo()etdeleteInfo(), mais ces trois méthodes levaient encore uneLogicException.- aucune capacité d'écriture n'existait dans le contrat public pour
commune_info_sections. MairieCommuneControllerdéléguait encore les écriturescommune_infosàMairieWriteService.AdminCommuneInfoControllerécrivait encore directement danscommune_infosetcommune_info_sections.
2. Arbitrage validé
commune_info_sectionsappartient à Municipal Management.- toutes les écritures
commune_infosetcommune_info_sectionscôté Mairie/Admin passent désormais parMunicipalManagementWriter. - Territory/SIRENE reste explicitement hors périmètre J2 ; sa réconciliation vers
commune_infosest reportée à J3.
3. Extension du contrat
Signatures ajoutées
public function createInfoSection(array $data): MunicipalInfoSectionDTO;
public function updateInfoSection(string $sectionId, array $data): MunicipalInfoSectionDTO;
public function deleteInfoSection(string $sectionId): void;
Signatures commune_infos désormais implémentées
public function createInfo(string $communeId, array $data): MunicipalInfoDTO;
public function updateInfo(string $communeId, string $infoId, array $data): MunicipalInfoDTO;
public function deleteInfo(string $communeId, string $infoId): void;
4. Écritures commune_infos migrées
Mairie
MairieCommuneController::storeInfo()MairieCommuneController::updateInfo()MairieCommuneController::destroyInfo()
Comportement conservé :
- mêmes routes ;
- mêmes codes HTTP ;
- mêmes payloads ;
- aucune validation Laravel ajoutée ;
- mêmes valeurs par défaut historiques (
section,icon_color,ordre) ; - même format JSON de réponse.
Évolution de sûreté conservatrice :
- l'isolation inter-communes est désormais garantie sur
updateInfo()via une lecture de retour scopée parcommune_id.
Admin
AdminCommuneInfoController::storeInfo()AdminCommuneInfoController::updateInfo()AdminCommuneInfoController::destroyInfo()
Comportement conservé :
- validations Laravel historiques inchangées ;
- messages d'erreur historiques inchangés pour la façade ;
- mêmes routes ;
- mêmes structures JSON ;
- aucune écriture directe restante vers
commune_infosdans la façade.
5. Écritures commune_info_sections migrées
AdminCommuneInfoController::storeSection()AdminCommuneInfoController::updateSection()AdminCommuneInfoController::destroySection()
Comportement conservé :
- validation Laravel inchangée ;
- contrôle d'unicité du slug inchangé dans la façade Admin ;
- mêmes valeurs par défaut (
icone,ordre,active) ; - mêmes codes HTTP ;
- mêmes formats JSON ;
- aucune écriture directe restante vers
commune_info_sectionsdans la façade.
6. Responsabilités restantes
Mairie
- autorisation via middleware inchangée ;
- adaptation HTTP ;
- vérification d'existence de la commune pour la création ;
- délégation vers
MunicipalManagementWriter.
Admin
- validation Laravel ;
- contrôles d'existence et messages d'erreur historiques ;
- contrôle d'unicité du slug ;
- construction des réponses agrégées (
fullCommune()) ; - délégation vers
MunicipalManagementReader/MunicipalManagementWriter.
Territory
Chemins volontairement laissés en place pour J3 :
CommuneMairieDataRefreshService::refreshInfos()CommuneMairieDataRefreshService::upsertInfo()
Ces écritures restent limitées à la section identite issue de SIRENE et n'ont pas été modifiées dans J2.
7. Classification des contrôles
MairieCommuneController
storeInfo():- validation syntaxique HTTP : aucune validation Laravel historique ;
- autorisation : middleware
commune.manager; - adaptation de réponse : sérialisation JSON
['info' => ...], statut201; - invariant métier : aucun restant ;
- contrainte de persistance : aucune restante.
updateInfo():- validation syntaxique HTTP : aucune validation Laravel historique ;
- autorisation : middleware
commune.manager; - adaptation de réponse : sérialisation JSON
['info' => ...], statut200; - invariant métier : aucun restant ;
- contrainte de persistance : aucune restante.
destroyInfo():- validation syntaxique HTTP : aucune ;
- autorisation : middleware
commune.manager; - adaptation de réponse : conservation du
204historique, y compris si la ressource est absente ; - invariant métier : aucun restant dans la façade ; l’existence ciblée est vérifiée dans Municipal Management puis volontairement traduite en suppression idempotente côté Mairie ;
- contrainte de persistance : aucune restante.
AdminCommuneInfoController
storeSection():- validation syntaxique HTTP :
validate()Laravel sur types et formats ; - autorisation : garde Admin existante ;
- adaptation de réponse :
201+ message historiqueCe slug existe déjà.en cas de doublon ; - invariant métier : l’unicité du slug vit désormais dans Municipal Management ;
- contrainte de persistance : aucune restante.
- validation syntaxique HTTP :
updateSection()/destroySection():- validation syntaxique HTTP : filtrage des champs modifiables ;
- autorisation : garde Admin existante ;
- adaptation de réponse : message historique
Rubrique introuvable.+404; - invariant métier : l’existence de la rubrique ciblée vit désormais dans Municipal Management ;
- contrainte de persistance : aucune restante.
storeInfo():- validation syntaxique HTTP :
validate()Laravel ; - autorisation : garde Admin existante ;
- adaptation de réponse :
201et format JSON historique ; - invariant métier : l’existence de la commune cible vit désormais dans Municipal Management ;
- contrainte de persistance : aucune restante.
- validation syntaxique HTTP :
updateInfo()/destroyInfo():- validation syntaxique HTTP : filtrage des champs modifiables ;
- autorisation : garde Admin existante ;
- adaptation de réponse : message historique
Info introuvable.+404; - invariant métier : existence de l’info ciblée et isolation inter-communes vivent désormais dans Municipal Management ;
- contrainte de persistance : aucune restante.
MunicipalManagementWriteService
- vérification d’existence de la commune pour
createInfo():- classification : invariant métier Municipal Management adossé à une contrainte de persistance ;
- justification : la création d’une info n’est valide que pour une commune existante.
- unicité du slug pour
createInfoSection():- classification : invariant métier Municipal Management ;
- justification : la validité métier du référentiel global impose un slug unique.
- existence de l’info ciblée pour
updateInfo()/deleteInfo():- classification : invariant métier Municipal Management ;
- justification : impossible de modifier ou supprimer une info inexistante ou appartenant à une autre commune.
- isolation inter-communes pour
updateInfo()/deleteInfo():- classification : invariant métier Municipal Management ;
- justification : l’opération doit être refusée si la ressource appartient à une autre commune.
- valeurs par défaut
section,icon_color,ordredanscreateInfo():- classification : invariant métier Municipal Management conservant le comportement historique ;
- justification : la façade Mairie n’applique aucune validation/défaut explicite.
- existence de la section ciblée pour
updateInfoSection()/deleteInfoSection():- classification : invariant métier Municipal Management ;
- justification : impossible de modifier ou supprimer une rubrique inexistante.
- valeurs par défaut
icone,ordre,activedanscreateInfoSection():- classification : invariant métier Municipal Management ;
- justification : elles déterminent l’état métier initial du référentiel.
- contrainte avant suppression :
commune_infos: existence ciblée et appartenance à la commune ;commune_info_sections: existence ciblée uniquement ;- aucune autre contrainte métier de suppression confirmée dans le code observé.
8. Logique déplacée ou conservée
Logique déplacée vers Municipal Management
- unicité du slug ;
- existence de la commune pour
createInfo(); - existence de l’info ciblée ;
- existence de la section ciblée ;
- isolation inter-communes sur
commune_infos; - valeurs métier par défaut des créations
commune_infos; - valeurs métier par défaut des créations
commune_info_sections; - contrainte de suppression sur ressource ciblée.
Logique conservée dans les façades
- validation Laravel des types et formats ;
- autorisation ;
- adaptation des statuts HTTP ;
- adaptation des messages historiques ;
- conservation explicite du
204idempotent côté Mairie surdestroyInfo()malgré l’erreur métier interne.
9. Preuve d’ownership métier unique
- les invariants ci-dessus ne sont plus évalués en SQL direct dans
MairieCommuneControllerouAdminCommuneInfoController; MunicipalManagementWriteServicedevient l’unique point qui décide si une écriturecommune_infosoucommune_info_sectionsest valide ;- les façades ne font plus que valider la forme HTTP, autoriser, déléguer, puis traduire l’erreur métier en réponse historique.
10. Compatibilité préservée
- routes inchangées ;
- URLs inchangées ;
- méthodes HTTP inchangées ;
- validations inchangées ;
- réponses JSON inchangées ;
- codes HTTP inchangés ;
- aucun frontend modifié ;
- aucune écriture Territory déplacée.
11. Tests exécutés
- création Mairie d'une info avec défauts historiques préservés ;
- modification Mairie avec isolation inter-communes ;
- suppression Mairie idempotente ;
- création / modification / suppression Admin de
commune_infos; - création / modification / suppression Admin de
commune_info_sections; - unicité du slug directement dans Municipal Management ;
- ressource inexistante directement dans Municipal Management ;
- ressource inexistante via Admin ;
- validation et unicité du slug côté Admin ;
- formats de réponse inchangés ;
- retour de DTO publics, jamais de modèle Eloquent ;
- garde-fous de délégation vers
MunicipalManagementWriter; - garde-fous d'unicité d'implémentation métier ;
- garde-fous d'absence de duplication des règles métier dans les deux façades ;
- garde-fous confirmant l'absence de modification Territory ;
- garde-fous confirmant les routes historiques inchangées.
12. Garde-fous ajoutés
- plus aucune écriture directe
commune_infosdansMairieCommuneController; - plus aucune écriture directe
commune_infosoucommune_info_sectionsdansAdminCommuneInfoController; - plus aucun contrôle direct de slug, d'existence de commune ou d'existence d'info/rubrique dans les écritures des façades J2 ;
MairieWriteServicen'expose plus de méthodes mortescreateInfo(),updateInfo(),deleteInfo();MunicipalManagementWriteServiceest l'unique implémentation métier des écritures J2 ;CommuneMairieDataRefreshServicereste intact pour J3.
13. Décisions restantes pour J3
- définir le contrat de réconciliation externe pour
commune_infos; - déplacer
CommuneMairieDataRefreshService::refreshInfos()vers Municipal Management ; - préciser la stratégie de convergence entre mises à jour manuelles et enrichissement SIRENE sur la section
identite.