Aller au contenu principal

ENG-001.5A — Fondation de Municipal Management — Rapport d'implémentation

MissionMunicipal-A — Fondation de Municipal Management
Rattaché àENG-001.5 — Extraction de Municipal Management §12
Dépôtdmv_api
Branchefeature/eng-001-5a-municipal-management-foundation (basée sur main, aucune dépendance à PR #14 ENG-001.4)
StatutImplémenté
Comportement fonctionnelInchangé (vérifié — 456 tests / 1702 assertions, 0 échec, 449/1690 préexistants + 7 nouveaux)

0. Écart procédural préalable

Le fichier dmv-docs/docs/19-engineering/specs/ENG-001-5A-municipal-management-foundation.md, cité comme lecture obligatoire, n'existe pas dans le dépôt (recherche exhaustive, y compris variantes de casse). Traité comme en ENG-001.3 : le corps de la mission redéfinit intégralement le périmètre, et son contenu est cohérent avec ENG-001-5-municipal-management.md §12 (« PR Municipal-A — Création du module (risque quasi nul) »), déjà validée. L'implémentation a donc pu procéder sans blocage sur ce point, à l'exception d'une divergence de nommage précisée en §1.


1. Divergence de nommage des contrats, résolue par les documents déjà validés

Constat. La mission propose, à titre d'exemple explicitement modifiable (« à adapter si la spec diffère ») : MunicipalAlertReader, MunicipalAlertWriter, MunicipalServiceReader, MunicipalServiceWriter — quatre contrats fragmentés par ressource, avec « Municipal » comme préfixe raccourci.

Ceci contredit deux éléments déjà validés et référencés par cette même mission :

  1. ENG-001-5-municipal-management.md §10 (« Contrats futurs ») cite explicitement MunicipalManagementReader/MunicipalManagementWriter — un seul pair par Bounded Context, repris de RFC-001.
  2. ENG-001-2-contrats-inter-contextes.md (règle explicite pour Municipal Management) : « Nommage aligné sur celui déjà acté dans ADR-014, 28-dmv-core-bounded-context-map.md et 30-dmv-core-migration-roadmap.mdne jamais raccourcir en « Municipal » dans le code ou la documentation. »
  3. Convention déjà appliquée sans exception à chaque Bounded Context existant : ActorReader/ ActorWriter, PublicationReader/PublicationWriter, TerritoryReader, SubscriptionManager — un seul Reader et au plus un seul Writer par contexte, jamais un contrat par ressource individuelle.

Traitement. La mission elle-même invite explicitement à adapter les noms « si la spec diffère » — ici, la « spec » (au sens large : les documents déjà validés référencés par la mission) diffère de façon documentée et cohérente sur trois sources indépendantes, pas par supposition. Retenu : MunicipalManagementReader et MunicipalManagementWriter, un seul pair, méthodes multiples par ressource (getAlertes, getServices, getElus, getCollectes, getInfos côté lecture ; méthodes CRUD équivalentes côté écriture). Non traité comme une décision d'architecture nouvelle : c'est l'application d'une décision déjà prise, pas l'invention d'une nouvelle.


2. Fichiers créés

Module MunicipalManagement

FichierRôle
app/Modules/MunicipalManagement/Contracts/MunicipalManagementReader.phpContrat de lecture — 5 méthodes (getAlertes, getServices, getElus, getCollectes, getInfos), chacune string $communeIdCollection du DTO correspondant
app/Modules/MunicipalManagement/Contracts/MunicipalManagementWriter.phpContrat d'écriture — 14 méthodes CRUD (3 alertes, 2 services, 3 élus, 3 collectes, 3 infos)
app/Modules/MunicipalManagement/DTOs/MunicipalAlertDTO.phpChamps alignés sur MairieAlerteDTO existant
app/Modules/MunicipalManagement/DTOs/MunicipalServiceDTO.phpChamps alignés sur MairieServiceDTO existant
app/Modules/MunicipalManagement/DTOs/MunicipalEluDTO.phpChamps alignés sur le modèle Territory\Models\CommuneElu
app/Modules/MunicipalManagement/DTOs/MunicipalCollecteDTO.phpChamps alignés sur le modèle Territory\Models\CommuneCollecte
app/Modules/MunicipalManagement/DTOs/MunicipalInfoDTO.phpChamps alignés sur le modèle Territory\Models\CommuneInfosection inclus comme chaîne brute, sans référence vers commune_info_sections (ownership non tranché, ENG-001.4 §11.C / ENG-001.5 §13.C)
app/Modules/MunicipalManagement/Services/MunicipalManagementReadService.phpImplémente MunicipalManagementReader — chaque méthode lève LogicException
app/Modules/MunicipalManagement/Services/MunicipalManagementWriteService.phpImplémente MunicipalManagementWriter — chaque méthode lève LogicException
app/Modules/MunicipalManagement/Providers/MunicipalManagementServiceProvider.phpBindings singleton + alias des deux contrats ; boot() ne charge aucune route
tests/Feature/MunicipalManagement/MunicipalManagementFoundationTest.php7 tests d'infrastructure (voir §6)

Aucun fichier créé sous Controllers/, Models/, ou Routes/ : ces répertoires ne sont pas créés du tout (git ne suit pas les répertoires vides, et la mission autorise explicitement l'absence de contrôleurs et interdit modèles/routes à ce stade).

Documentation

FichierContenu
dmv-docs/docs/19-engineering/specs/ENG-001-5A-implementation-report.mdCe document

3. Fichiers modifiés

FichierChangement
api/bootstrap/providers.phpAjout de MunicipalManagementServiceProvider::class à la liste des providers actifs, après RewardsModuleServiceProvider

Aucun autre fichier applicatif touché — confirmé par git status (voir §8).


4. Arborescence créée

app/Modules/MunicipalManagement/
├── Contracts/
│ ├── MunicipalManagementReader.php
│ └── MunicipalManagementWriter.php
├── DTOs/
│ ├── MunicipalAlertDTO.php
│ ├── MunicipalCollecteDTO.php
│ ├── MunicipalEluDTO.php
│ ├── MunicipalInfoDTO.php
│ └── MunicipalServiceDTO.php
├── Providers/
│ └── MunicipalManagementServiceProvider.php
└── Services/
├── MunicipalManagementReadService.php
└── MunicipalManagementWriteService.php

Controllers/, Models/, Routes/ intentionnellement absents (voir §2).


5. Contrats créés

Voir §1 pour la résolution du nommage. Récapitulatif des signatures :

MunicipalManagementReader : getAlertes, getServices, getElus, getCollectes, getInfos — chacune (string $communeId): Collection.

MunicipalManagementWriter : createAlerte, updateAlerte, desactiverAlerte, createService, updateService, createElu, updateElu, deleteElu, createCollecte, updateCollecte, deleteCollecte, createInfo, updateInfo, deleteInfo.

Signatures dérivées des méthodes déjà existantes de MairieWriteService/MairieReadService/ TerritoryService (mêmes noms de ressources, mêmes paramètres), sans reprendre leur code. Écart volontaire par rapport à MairieWriteService : le paramètre Profile $user (présent sur chaque méthode d'écriture de MairieWriteService mais jamais utilisé dans leurs corps actuels, vérifié) n'a pas été repris — l'exposer dans un contrat public violerait ADR-014 §12 (« si un service applicatif reçoit... un modèle Eloquent... une frontière a été violée »).


6. DTO créés

5 DTO, tous immuables (readonly), tous avec toArray(), aucun avec fromModel() — volontaire : créer un fromModel() référencerait un modèle Eloquent d'un autre module (Mairie\Models\MairieAlerte, Territory\Models\CommuneElu, etc.), ce qui créerait dès la fondation un couplage vers des modules dont la donnée n'a pas encore été migrée. Reporté à la PR qui réalisera effectivement chaque migration (Municipal-C/D/E). Voir §2 pour le détail champ par champ de chaque DTO.


7. Bindings créés

Dans MunicipalManagementServiceProvider::register() :

$this->app->singleton(MunicipalManagementReadService::class);
$this->app->singleton(MunicipalManagementWriteService::class);
$this->app->alias(MunicipalManagementReadService::class, MunicipalManagementReader::class);
$this->app->alias(MunicipalManagementWriteService::class, MunicipalManagementWriter::class);

Provider enregistré dans bootstrap/providers.php. Aucun binding vers Mairie, Territory ou Admin — le module ne dépend d'aucun autre module à ce stade.


8. Tests créés

tests/Feature/MunicipalManagement/MunicipalManagementFoundationTest.php, 7 tests, tous d'infrastructure (aucun test métier, conformément à la mission) :

  1. Le module se charge sans erreur (bootstrap complet).
  2. MunicipalManagementReader résolu par le conteneur → instance de MunicipalManagementReadService.
  3. MunicipalManagementWriter résolu par le conteneur → instance de MunicipalManagementWriteService.
  4. Les deux services sont des singletons (même instance sur deux résolutions).
  5. Chacune des 5 méthodes de lecture lève LogicException avec un message explicite, sans effet de bord.
  6. createAlerte() (représentatif des 14 méthodes d'écriture) lève LogicException.
  7. Aucune route dont l'URI contient municipal n'est enregistrée (app('router')->getRoutes()).

9. Validations exécutées

CommandeRésultat
php -l sur les 11 fichiers créés/modifiésOK
php artisan about (bootstrap complet)OK
Résolution container (MunicipalManagementReader/Writer → services, via php artisan tinker, y compris déclenchement effectif de LogicException)OK
php artisan route:list --except-vendor | grep municipalVide — confirmé aucune route
vendor/bin/pint --test sur le module, bootstrap/providers.php et les testsOK (1 correction automatique appliquée : guillemets simples dans le nouveau test, revérifié après correction)
php artisan test --filter=MunicipalManagementFoundationTest7 tests, 12 assertions — vert
php artisan test (suite complète)456 tests, 1702 assertions, 0 échec (449/1690 préexistants sur main + 7 nouveaux)
vendor/bin/phpstan analyseAbsent du projet (vendor/bin/phpstan inexistant), comme constaté à chaque mission précédente
composer analyseNon défini dans composer.json

10. Divergences

  1. §0 — fichier de lecture obligatoire manquant, non bloquant, même traitement qu'ENG-001.3.
  2. §1 — nommage des contrats résolu en faveur de MunicipalManagementReader/Writer plutôt que les quatre noms donnés en exemple par la mission, sur la base de trois sources déjà validées et cohérentes entre elles (spec ENG-001.5, règle explicite d'ENG-001.2, convention uniforme du reste du code). Documentée avant implémentation, pas après.

Aucune autre divergence. Aucune décision d'ownership, de migration de données, ou de contrat vers un module inexistant n'a été prise — le module reste vide de toute logique réelle, exactement comme demandé.


11. Confirmation qu'aucun comportement fonctionnel n'a changé

  • Le module n'est atteint par aucun code existant : aucun fichier de Mairie, Territory, Actor, Admin ne référence MunicipalManagement (vérifié — le nouveau module n'est importé nulle part hors de lui-même et de bootstrap/providers.php).
  • La suite de tests complète, qui exerce l'intégralité du comportement observable actuel de l'application, reste intégralement verte : 456/456, dont les 449 tests préexistants sur main strictement inchangés.
  • Toute méthode du nouveau module, si jamais appelée par erreur depuis un code futur non prévu par cette PR, échoue bruyamment (LogicException) plutôt que de produire un comportement silencieusement incorrect.

12. Confirmation qu'aucun endpoint public n'a été modifié

  • Aucune route créée : Routes/ n'existe pas dans le nouveau module, boot() du Service Provider ne charge aucun fichier de routes.
  • Vérifié par php artisan route:list --except-vendor (aucune entrée municipal) et par le test automatisé dédié (§8, test 7).
  • Aucune route existante modifiée, aucun contrôleur existant touché.

13. Confirmation qu'aucun commit n'a été créé

Branche feature/eng-001-5a-municipal-management-foundation créée depuis main à jour, tous les fichiers ci-dessus présents en working tree, aucun git add/git commit exécuté. git status ne montre que les fichiers de cette mission plus les deux fichiers pré-existants et sans rapport (database/seeders/DatabaseSeeder.php, database/seeders/ActorNotorietyConfigSeeder.php, inchangés depuis les missions précédentes, non créés par cette mission).