ENG-001.5C — Retrait de la duplication des alertes municipales — Rapport d'implémentation
| Mission | ENG-001.5C — Retrait de la duplication des alertes municipales |
| Rattaché à | ENG-001.5 — Extraction de Municipal Management ; ENG-001.5B — Rapport d'implémentation ; Audit API alertes |
| Dépôt | dmv_api |
| Branche | feature/eng-001-5c-remove-municipal-alert-duplication (basée sur main + fondations Municipal-A/B non commitées) |
| Statut | Implémenté |
| Comportement fonctionnel | Inchangé pour les routes Actor (façade publique). Routes Mairie dédiées aux alertes supprimées (zéro consommateur identifié). |
0. Écart procédural préalable
ENG-001-5A-municipal-management-foundation.md, cité comme lecture obligatoire, reste introuvable
(déjà signalé en ENG-001.5A et ENG-001.5B, non résolu depuis). Non bloquant : le corps de la
mission, combiné à ENG-001-5-municipal-management.md et aux rapports ENG-001.5A/5B déjà produits,
suffit à cadrer le travail sans ambiguïté.
1. Fichiers supprimés
| Fichier | Raison |
|---|---|
app/Modules/Mairie/Controllers/MairieAlerteController.php | 100 % logique alertes, appelait des méthodes de service désormais supprimées ; aucune route ne le référence plus |
app/Modules/Mairie/Requests/CreateAlerteRequest.php | Utilisé uniquement par MairieAlerteController::store() |
app/Modules/Mairie/Requests/UpdateAlerteRequest.php | Utilisé uniquement par MairieAlerteController::update() |
app/Modules/Mairie/DTOs/MairieAlerteDTO.php | Utilisé uniquement par les méthodes alertes de MairieReadService/MairieWriteService, elles-mêmes supprimées |
app/Modules/Mairie/Models/MairieAlerte.php | Modèle Eloquent devenu orphelin une fois expireAlertes() relocalisée hors Eloquent (§3) ; vérifié : aucune relation d'un autre modèle ne pointe vers MairieAlerte::class |
Vérifié après chaque suppression (grep -rln sur app/, tests/, routes/) : zéro référence
résiduelle en dehors des mentions descriptives dans les docblocks de Municipal Management (§3).
2. Fichiers modifiés
| Fichier | Changement |
|---|---|
app/Modules/Mairie/Services/MairieWriteService.php | Suppression de createAlerte(), updateAlerte(), desactiverAlerte(), expireAlertes() (relocalisée, §3) et du helper privé devenu mort getCommuneIdForActeur(). Imports MairieAlerteDTO/MairieAlerte retirés. Services/publications/commune/élus/collectes/infos strictement inchangés. |
app/Modules/Mairie/Services/MairieReadService.php | Suppression de listAlertes() et getAlerte() (cette dernière déjà sans aucun appelant avant cette PR — confirmé par grep). Imports MairieAlerteDTO/MairieAlerte retirés. Services/commune/élus/collectes/infos/publications strictement inchangés. |
app/Modules/Mairie/Routes/api.php | Retrait des 4 routes /mairie/communes/{communeId}/alertes, /mairie/acteurs/{acteurId}/alertes, /mairie/alertes/{alerteId}, /mairie/alertes/{alerteId}/desactiver, et de l'import MairieAlerteController. Les autres routes Mairie (services, publications, commune, élus, collectes, infos) inchangées. |
app/Modules/Mairie/Jobs/ExpireAlertes.php | Dépend désormais de MunicipalManagementWriter au lieu de MairieWriteService. Reste dans Mairie\Jobs pour ne pas modifier la déclaration du scheduler dans bootstrap/app.php (référencée par nom de classe, inchangée). |
app/Modules/MunicipalManagement/Contracts/MunicipalManagementWriter.php | Ajout de expireAlertes(): int (voir §3). |
app/Modules/MunicipalManagement/Services/MunicipalManagementWriteService.php | Implémentation de expireAlertes() (voir §3). Docblock de classe mis à jour. |
tests/Feature/Mairie/MairieTest.php | Retrait des 7 tests alertes (publics ×2, protégés ×5) et du helper createAlerte() devenu mort. Import MairieWriteService retiré (devenu inutilisé). Helper createActeurAutre() retiré (devenu orphelin — n'était utilisé que par un test alertes supprimé). Tests services/publications/commune inchangés. |
tests/Feature/MunicipalManagement/MunicipalAlertDelegationTest.php | Ajout du test expire_alertes_desactive_les_alertes_expirees_via_municipal_management_writer (déplacé/adapté depuis MairieTest.php, voir §3) et du garde-fou il_nexiste_plus_quune_seule_implementation_metier_des_alertes (§6). |
tests/Feature/Actor/ActorAccessPhaseOneTest.php | Retrait de test_mairie_alert_route_requires_module_activation, qui exerçait la route Mairie désormais supprimée (POST /mairie/acteurs/{acteurId}/alertes). Couverture équivalente déjà présente côté route Actor : MunicipalAlertDelegationTest::creation_avec_module_alertes_desactive_retourne_403. Découvert par l'exécution de la suite complète, non par l'audit initial de fichiers (voir §7). |
3. Logique supprimée / relocalisée
Supprimée définitivement (duplication pure, aucun remplacement nécessaire)
Les méthodes MairieReadService::listAlertes()/getAlerte() et
MairieWriteService::createAlerte()/updateAlerte()/desactiverAlerte() faisaient doublon exact
avec MunicipalManagementReadService/WriteService, déjà seules invoquées par la façade Actor
depuis ENG-001.5B. Elles n'avaient plus aucun appelant : la route publique Mairie était leur seul
consommateur, et cette route n'a elle-même aucun consommateur frontend (voir §5).
Relocalisée : expireAlertes()
MairieWriteService::expireAlertes() avait un appelant réel — le Job planifié
Mairie\Jobs\ExpireAlertes (bootstrap/app.php, hourly()). Ce n'était donc pas du code mort,
mais de la logique métier « Alertes » restée hors de Municipal Management, en contradiction avec
la cible explicite de la mission (« Aucune logique métier "Alertes" ne doit rester ailleurs »).
Décision : relocaliser vers MunicipalManagementWriter::expireAlertes(), avec un comportement
strictement identique (même requête, reconstituée à l'identique depuis le code original) :
public function expireAlertes(): int
{
return DB::table('mairie_alertes')
->whereRaw('"is_active" IS TRUE')
->whereNotNull('expires_at')
->where('expires_at', '<=', now())
->update(['is_active' => DB::raw('false')]);
}
Le Job ExpireAlertes est mis à jour pour injecter MunicipalManagementWriter au lieu de
MairieWriteService, sans être déplacé de module : il reste une façade fine dans Mairie\Jobs qui
délègue via le contrat, sur le même principe que ActeurModulesController (façade Actor qui
délègue à Municipal Management sans porter de logique métier). Le déplacer physiquement de module
aurait nécessité de modifier la déclaration du scheduler dans bootstrap/app.php, hors du
périmètre strict de cette mission et sans bénéfice architectural supplémentaire (ADR-014 n'impose
pas l'emplacement des adaptateurs, seulement l'ownership de la logique métier).
4. Logique conservée
- Toute la logique services municipaux, élus, collectes, infos pratiques, commune, publications
dans
MairieReadService/MairieWriteService/MairieCommuneController/MairieServiceController/MairiePublicationController— strictement hors périmètre, non touchée. - Les 4 routes Actor (façade publique) :
GET/POST /acteurs/{acteurId}/alertes,PATCH /acteurs/{acteurId}/alertes/{alerteId},PATCH .../desactiver— inchangées depuis ENG-001.5B, revérifiées parphp artisan route:list(§7). ActeurModulesController,MunicipalManagementReader/Writer(hors ajoutexpireAlertes()),MunicipalManagementReadService/WriteService(hors ajoutexpireAlertes()) — inchangés, travail déjà réalisé en ENG-001.5A/5B.
5. Vérification des consommateurs (avant suppression des routes Mairie)
Conformément à l'instruction explicite de la mission (« avant chaque suppression : vérifier les
consommateurs »), une vérification fraîche a été effectuée dans cette mission, en plus de l'audit
déjà produit par ENG-001-5-alert-api-consumer-audit.md :
- Backend —
grep -rnsurapp/,routes/pour les URIsmairie/communes/.../alertes,mairie/acteurs/.../alertes,mairie/alertes/...: aucune occurrence en dehors du contrôleur/des routes supprimés eux-mêmes. - Frontends (
dmv-workspace,dmv-public,dmv-backoffice,dmv_backoffice, horsnode_modules/.next/dist/build) — un seul faux positif détecté :dmv-public/app/components/wall/hooks/useWallUserSignals.tscontient une variable locale nomméemairieAlertes, mais appelle en réalitéGET /acteurs/{acteurId}/alertes(route Actor, inchangée) — vérifié en lisant le code source de la fonction. Aucun consommateur réel des routes Mairie n'a été trouvé.
Confirme et complète le constat de l'audit ENG-001.5 : zéro consommateur des routes Mairie dédiées aux alertes.
6. Tests créés
Dans tests/Feature/MunicipalManagement/MunicipalAlertDelegationTest.php :
expire_alertes_desactive_les_alertes_expirees_via_municipal_management_writer— migré/adapté depuisMairieTest::expire_alertes_job_desactive_alertes_expirees, appelle désormaisMunicipalManagementWriter::expireAlertes()au lieu deMairieWriteService::expireAlertes(). Mêmes assertions (1 alerte expirée désactivée, alerte future et alerte sans expiration inchangées).il_nexiste_plus_quune_seule_implementation_metier_des_alertes— garde-fou explicitement demandé par la mission (« Ajouter un test garantissant qu'il n'existe plus qu'une seule implémentation métier des alertes »). Vérifie :MairieAlerteController,MairieAlerte(modèle),MairieAlerteDTO,CreateAlerteRequest,UpdateAlerteRequestn'existent plus (class_exists(..., false)— sans déclencher l'autoloader, pour ne pas provoquer d'erreur sur une classe volontairement supprimée) ;MairieWriteServicen'expose pluscreateAlerte/updateAlerte/desactiverAlerte/expireAlertes;MairieReadServicen'expose pluslistAlertes/getAlerte;- aucune route enregistrée sous
api/v1/mairie/...ne contient plus le mot « alerte » ; MunicipalManagementWriter(résolu depuis le conteneur) expose biencreateAlerteetexpireAlertes.
Retrait net de 7 tests obsolètes dans MairieTest.php (§2) et de 1 test obsolète dans
ActorAccessPhaseOneTest.php (§2, §7) — remplacés par une couverture équivalente ou supérieure
côté Municipal Management/Actor.
7. Validations exécutées
| Commande | Résultat |
|---|---|
php -l sur tous les fichiers créés/modifiés | OK |
php artisan route:list --path=mairie | 28 routes, aucune ne contient « alerte » |
php artisan route:list --path=acteurs | grep alerte | 4 routes Actor, strictement identiques à avant cette PR |
vendor/bin/pint --test (fichiers touchés) | OK après 2 corrections automatiques (MairieWriteService.php, MunicipalAlertDelegationTest.php — la seconde a converti mes références \Fully\Qualified\Class::class en imports use + noms courts, comportement standard du fixer fully_qualified_strict_types, sans effet fonctionnel car Class::class ne déclenche pas l'autoload) |
vendor/bin/pint --test (repo entier) | 1 seul fichier signalé : database/seeders/ActorNotorietyConfigSeeder.php, pré-existant, sans rapport avec cette mission (déjà signalé dans tous les rapports précédents) |
php artisan test (suite complète, 1ère exécution) | 462 tests — 1 échec (ActorAccessPhaseOneTest::test_mairie_alert_route_requires_module_activation, testait la route Mairie supprimée) + 1 erreur (class_exists() sans 2ᵉ argument déclenchait l'autoload sur une classe volontairement supprimée) |
| Corrections apportées | Retrait du test obsolète (§2) ; ajout de false en 2ᵉ argument de class_exists() |
php artisan test (suite complète, exécution finale) | 461 tests, 1730 assertions, 0 échec |
vendor/bin/phpstan analyse | Absent du projet, comme constaté à chaque mission précédente |
composer analyse | Non défini dans composer.json |
8. Divergences
- §0 — fichier de lecture obligatoire manquant, non bloquant (déjà signalé en ENG-001.5A/5B).
- Relocalisation de
expireAlertes()vers Municipal Management (§3) — la mission ne mentionnait pas explicitement cette méthode, mais son maintien dansMairieaurait laissé de la logique métier « Alertes » hors de Municipal Management, contredisant directement l'objectif de clôture énoncé par la mission (« une seule implémentation métier », « Aucune logique métier "Alertes" ne doit rester ailleurs »). Traité comme une extension mineure et cohérente du travail déjà engagé en ENG-001.5B (même contrat, même service), pas comme une nouvelle décision d'architecture — signalé ici conformément à la gouvernance. - Suppression des 4 routes Mairie dédiées aux alertes — impact potentiel sur des contrats HTTP publics, donc signalé explicitement : vérifié à deux reprises (audit initial ENG-001.5 + re-vérification fraîche dans cette mission, §5) qu'aucun frontend ni service backend ne les consomme. Décision prise sur la base de cette double vérification, conformément à l'autorisation explicite de la mission (« Si l'API Mairie n'a réellement plus aucun consommateur : préparer sa suppression »).
- Suppression du test
test_mairie_alert_route_requires_module_activation— un test pré-existant hors du périmètre initialement audité (tests/Feature/Actor/ActorAccessPhaseOneTest.php) exerçait une route désormais supprimée. Retiré après vérification qu'une couverture équivalente existe déjà côté route Actor (§2, §6) — pas de perte de couverture fonctionnelle.
Aucune autre divergence. Aucune décision d'ownership nouvelle : la table mairie_alertes reste
physiquement dans le domaine de données historique, seule la couche applicative change.
9. Confirmations
Une seule implémentation métier des alertes existe désormais. MunicipalManagementReadService/
WriteService sont l'unique code qui lit/écrit mairie_alertes (hors la ligne SQL brute
expireAlertes(), également dans Municipal Management). Vérifié par le garde-fou automatisé
il_nexiste_plus_quune_seule_implementation_metier_des_alertes (§6) et par grep exhaustif (§1, §2).
Aucune route publique Actor n'a changé. Les 4 routes
(GET/POST /acteurs/{acteurId}/alertes, PATCH /acteurs/{acteurId}/alertes/{alerteId},
PATCH .../desactiver) existent avec les mêmes URIs, mêmes verbes, mêmes noms qu'avant cette PR —
vérifié par php artisan route:list. Les 4 routes Mairie dédiées aux alertes ont été supprimées
(zéro consommateur, §5) ; toutes les autres routes Mairie (services, publications, commune, élus,
collectes, infos) sont inchangées.
Aucun frontend ne nécessite de modification. Les routes Actor consommées par
dmv-workspace/dmv-public (identifiées par l'audit ENG-001.5 et revérifiées §5) sont strictement
inchangées. Les routes Mairie supprimées n'avaient aucun consommateur identifié, ni avant ni après
re-vérification.
Aucun commit n'a été créé. Branche feature/eng-001-5c-remove-municipal-alert-duplication,
tous les fichiers ci-dessus en working tree, aucun git add/git commit exécuté. git status ne
montre que les fichiers de cette mission et de Municipal-A/B (non committées non plus), plus les
deux fichiers pré-existants sans rapport déjà signalés dans tous les rapports précédents
(database/seeders/DatabaseSeeder.php, database/seeders/ActorNotorietyConfigSeeder.php).