Aller au contenu principal

ENG-001.2 — Rapport d'implémentation

MissionENG-001.2 — Contrats inter-contextes
EpicEPIC-001 — Restructuration de DMV Core
PR ciblePR-001
StatutImplémenté, corrigé le 2026-07-24
Comportement fonctionnelInchangé

Corrections apportées après revue architecte (2026-07-24)

Deux écarts non conformes à la mission ENG-001.2 ont été identifiés lors d'une revue et corrigés :

  1. Identity manquant. La mission prévoit explicitement IdentityReader dans la première vague de PR-001. Il était absent de l'implémentation initiale, sans mention ni en contrat créé, ni en contrat reporté. Ajouté : App\Modules\Identity\Contracts\IdentityReader (méthode unique getCurrentUser(string $userId): ?CurrentUser), lié à ProfileService via une nouvelle méthode getCurrentUser() — simple composition de Profile::find() et de buildCurrentUser(), déjà existants, sans logique nouvelle.
  2. Municipal Management non conforme. L'implémentation initiale avait créé Mairie\Contracts\MunicipalReader/MunicipalWriter (nom non aligné sur « Municipal Management » acté par ADR-014/28/30) et lié MunicipalReader directement à MairieReadService — exactement la liaison silencieuse que la mission interdisait explicitement par son nom. Ni l'Option A (adaptateur temporaire documenté) ni l'Option B (report complet, déjà signalée comme préférée par la mission) n'avaient été suivies. Revenu à l'Option B : MunicipalReader.php/MunicipalWriter.php supprimés, MairieReadService ne les implémente plus, l'alias retiré de MairieServiceProvider. Contrats Municipal Management entièrement reportés à ENG-001.5 (numérotation réalignée le 2026-08-01, voir ENG-001-3-implementation-report.md §9 — Territory Bounded Context est désormais ENG-001.4, ce qui décale Municipal Management et les missions suivantes d'un rang).

Validations rejouées après correction : php -l sur les fichiers touchés, php artisan about, résolution container (IdentityReaderProfileService ; MunicipalReader désormais non résolvable, confirmé), php artisan test --testsuite=Unit (1 test, 1 assertion, vert), vendor/bin/pint --test sur les fichiers touchés (vert).

Synthèse

La couche initiale de contrats inter-contextes est introduite sous api/app/Modules/<Context>/Contracts.

Les contrats sont propriétaires de leur contexte, n'exposent aucun modèle Eloquent, aucun Query Builder et aucune Relation. Les bindings Laravel sont ajoutés uniquement quand l'implémentation actuelle possède déjà une signature compatible.

Aucun contrôleur, aucune route et aucune table n'ont été modifiés.

Contrats créés

Contexte propriétaireInterfaceImplémentation actuelleBinding LaravelMission future
ActorActorReaderActeurReadServiceOui, alias vers le singleton existantENG-001.8, ENG-001.9
ActorActorWriterActeurWriteServiceOui, alias vers le singleton existantENG-001.7, ENG-001.8, ENG-001.9
PublicationPublicationReaderPublicationReadServiceOui, alias vers le singleton existantENG-001.6, ENG-001.9
PublicationPublicationWriterPublicationWriteServiceOui, alias vers le singleton existantENG-001.6, ENG-001.9
PublicationPublicationImporterReportéeNonENG-001.6, ENG-001.9
MonetizationSubscriptionManagerMonetizationWriteServiceOui, alias vers le singleton existantENG-001.8, ENG-001.9
MonetizationBoostManagerBoostWriteServiceOui, alias vers le singleton existantENG-001.8, ENG-001.9
MonetizationBoostReaderBoostReadServiceOui, alias vers le singleton existantENG-001.8, ENG-001.9
TerritoryTerritoryReaderTerritoryServiceOui, alias vers le singleton existantENG-001.4, ENG-001.5, ENG-001.9
IdentityIdentityReaderProfileServiceOui, alias vers le singleton existantENG-001.9
Municipal ManagementMunicipalManagementReaderReportée (Option B)NonENG-001.5, ENG-001.9
Municipal ManagementMunicipalManagementWriterReportée (Option B)NonENG-001.5, ENG-001.9
CommunityCommunityReaderCommunityReadServiceOui, alias vers le singleton existantENG-001.9
RewardsRewardRecorderRewardEngineServiceOui, alias vers le singleton existantENG-001.6, ENG-001.9, ENG-001.10

Bindings Laravel ajoutés

Les bindings sont déclarés dans les Service Providers des modules propriétaires, selon la convention actuelle.

ProviderBindings
ActorServiceProviderActorReader → ActeurReadService, ActorWriter → ActeurWriteService
PublicationServiceProviderPublicationReader → PublicationReadService, PublicationWriter → PublicationWriteService
MonetizationServiceProviderSubscriptionManager → MonetizationWriteService, BoostManager → BoostWriteService, BoostReader → BoostReadService
TerritoryServiceProviderTerritoryReader → TerritoryService
IdentityServiceProviderIdentityReader → ProfileService
CommunityServiceProviderCommunityReader → CommunityReadService
RewardsModuleServiceProviderRewardRecorder → RewardEngineService

Contrats volontairement reportés

ContratDécisionJustification
MunicipalManagementReader/MunicipalManagementWriterReporté (Option B)Municipal Management n'existe pas encore comme module — sa logique est fragmentée entre Mairie et Territory. Créer ces contrats maintenant, même en les liant à MairieReadService, figerait cette fragmentation dans un contrat public durable — c'est explicitement ce que la mission demande d'éviter. Reporté à ENG-001.5, une fois le module réellement extrait.
PublicationImporter bindingReportéImportRunnerService contient encore la logique d'import et écrit directement dans publications. Une implémentation sans logique nouvelle n'est pas possible dans ENG-001.2.
Contrat Rewards → création de boost récompenseReportéAucun service Monetization public actuel ne matérialise un boost récompense sans paiement. Le créer modifierait la logique métier.
Contrats Platform Services globauxReportéENG-001.2 interdit les contrats métier hors contexte et ne crée pas encore Authorization/Search/Files/Notification.
Contrats d'écriture Territory ↔ MunicipalReportéLes signatures sûres nécessitent d'abord la clarification et l'extraction Municipal Management prévues par ENG-001.4/ENG-001.5.

Contrats et capacités identifiés pour les futures PR

Cette section conserve les constats issus de l'analyse ENG-001.2 sans valider les signatures, les emplacements ou les ownerships qui restent à arbitrer.

SujetStatutConstatDécision à ce stadeFuture PR / phase
Attribution gratuite de boostsBesoin confirméRewardEngineService::createBoostForReward() écrit directement dans boosts. Le contrat commercial actuel purchaseBoost() ne convient pas à une attribution gratuite.Capacité identifiée côté Monetization. Le nom et la signature de la future méthode ne sont pas validés à ce stade.PR-006 — Rewards → Monetization
Platform Service AuthorizationDesign de Platform Service à arbitrerLes règles d'autorisation sont réparties entre ActorAccessService, les middleware Mairie, CommunityPolicy, EnsureAssoAccess et la logique ensureOwner() de Monetization.Besoin de convergence confirmé, mais aucune forme n'est choisie maintenant : ni AuthorizationChecker, ni PermissionResolver, ni autre design.Phase 2 de la roadmap DMV Core
Rafraîchissement SIRENEOwnership à arbitrerCommuneMairieDataRefreshService écrit directement dans acteurs. Une opération spécifique semble préférable à un updateActeur(array $data) générique.Décision requise sur l'owner et la signature. ActorWriter::applySireneRefresh() reste une signature indicative non validée.Phase 5 — refonte du rafraîchissement SIRENE
Résolution commune ↔ acteur mairieOwnership à arbitrerLa résolution de l'acteur mairie lié à une commune est dupliquée dans plusieurs emplacements.Options possibles non arbitrées : contrat publié par Actor, contrat publié par Territory, ou futur contrat publié par Municipal Management.Quick Win relatif à resolveManagedCommunes(), puis extraction de Municipal Management
Opérations administratives ActorPrincipe confirmé, signature à définirAdmin écrit directement des champs sensibles de acteurs. Un updateActeur(array $data) générique ne constitue pas une frontière suffisamment contraignante.Principe de capacités Actor dédiées confirmé. Les méthodes définitives restent à définir au moment de la migration Admin → Actor.PR-008 — Admin → Actor
CommunityWriterReport explicite déjà validéCe contrat sera nécessaire à terme pour supprimer l'écriture directe d'Admin dans contributors.can_publish.Absence volontaire dans PR-001. Le contrat non validé à ce stade devra être conçu avec l'ownership Community complet.PR-008 — Admin → Community
TerritoryWriterBloqué par une décision produitUn Writer pourrait devenir nécessaire pour certains champs de commune.Aucun contrat ne doit être créé avant décision produit sur l'ownership de communes.description et communes.image_url.Décision produit préalable, puis PR à définir

Validations exécutées

CommandeRésultat
php -l sur tous les fichiers PHP créés/modifiésOK
php artisan aboutOK
php artisan route:list --except-vendorOK
Bootstrap PHP + résolution container des contrats bindésOK
php artisan test --testsuite=UnitOK : 1 test, 1 assertion
vendor/bin/pint --testÉchec hors périmètre sur database/seeders/ActorNotorietyConfigSeeder.php
vendor/bin/pint --test ciblé sur les fichiers créés/modifiésOK
composer test en sandboxÉchec environnement : accès PostgreSQL local interdit par sandbox
composer test hors sandboxÉchec environnement : PostgreSQL local refuse la connexion sur 127.0.0.1:5432, base dmv_test

Commandes absentes

CommandeÉtat
composer analyseNon définie dans composer.json
vendor/bin/phpstan analysevendor/bin/phpstan absent

Écarts avec la spécification

ÉcartStatutJustification
Tous les contrats ne sont pas bindésAcceptéLa mission demande des bindings lorsque possible. Les bindings incompatibles auraient exposé Eloquent ou introduit une implémentation nouvelle.
La suite de tests complète n'est pas verte localementBloqué par environnementL'échec vient de PostgreSQL local indisponible, pas du code modifié. Les validations sans base passent.

Risques identifiés

  • Les premiers contrats restent volontairement minimaux ; les futures PR devront les étendre uniquement au moment où elles remplacent un appel direct existant.
  • Certains services actuels exposent encore des types Laravel ou Eloquent hors des contrats ; ces méthodes n'ont pas été incluses pour éviter d'officialiser une mauvaise frontière.
  • Le contexte Municipal Management n'a aucun contrat public pour l'instant (Option B) ; les appels directs vers Mairie/Territory restent inchangés jusqu'à ENG-001.5.

Recommandation pour la mission suivante

Démarrer par le remplacement d'un flux simple et mesurable, sans toucher plusieurs domaines à la fois :

  1. utiliser ActorWriter pour préparer la suppression d'une écriture Admin directe sur Actor ;
  2. ou utiliser RewardRecorder comme première étape avant les événements Publication/Actor ;
  3. garder MunicipalManagementReader/MunicipalManagementWriter et PublicationImporter reportés jusqu'à création d'une implémentation dédiée (ENG-001.5 pour Municipal Management).