ENG-001.2 — Rapport d'implémentation
| Mission | ENG-001.2 — Contrats inter-contextes |
| Epic | EPIC-001 — Restructuration de DMV Core |
| PR cible | PR-001 |
| Statut | Implémenté, corrigé le 2026-07-24 |
| Comportement fonctionnel | Inchangé |
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 :
- Identity manquant. La mission prévoit explicitement
IdentityReaderdans 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 uniquegetCurrentUser(string $userId): ?CurrentUser), lié àProfileServicevia une nouvelle méthodegetCurrentUser()— simple composition deProfile::find()et debuildCurrentUser(), déjà existants, sans logique nouvelle. - 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éMunicipalReaderdirectement à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.phpsupprimés,MairieReadServicene les implémente plus, l'alias retiré deMairieServiceProvider. 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 (IdentityReader → ProfileService ; 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étaire | Interface | Implémentation actuelle | Binding Laravel | Mission future |
|---|---|---|---|---|
| Actor | ActorReader | ActeurReadService | Oui, alias vers le singleton existant | ENG-001.8, ENG-001.9 |
| Actor | ActorWriter | ActeurWriteService | Oui, alias vers le singleton existant | ENG-001.7, ENG-001.8, ENG-001.9 |
| Publication | PublicationReader | PublicationReadService | Oui, alias vers le singleton existant | ENG-001.6, ENG-001.9 |
| Publication | PublicationWriter | PublicationWriteService | Oui, alias vers le singleton existant | ENG-001.6, ENG-001.9 |
| Publication | PublicationImporter | Reportée | Non | ENG-001.6, ENG-001.9 |
| Monetization | SubscriptionManager | MonetizationWriteService | Oui, alias vers le singleton existant | ENG-001.8, ENG-001.9 |
| Monetization | BoostManager | BoostWriteService | Oui, alias vers le singleton existant | ENG-001.8, ENG-001.9 |
| Monetization | BoostReader | BoostReadService | Oui, alias vers le singleton existant | ENG-001.8, ENG-001.9 |
| Territory | TerritoryReader | TerritoryService | Oui, alias vers le singleton existant | ENG-001.4, ENG-001.5, ENG-001.9 |
| Identity | IdentityReader | ProfileService | Oui, alias vers le singleton existant | ENG-001.9 |
| Municipal Management | MunicipalManagementReader | Reportée (Option B) | Non | ENG-001.5, ENG-001.9 |
| Municipal Management | MunicipalManagementWriter | Reportée (Option B) | Non | ENG-001.5, ENG-001.9 |
| Community | CommunityReader | CommunityReadService | Oui, alias vers le singleton existant | ENG-001.9 |
| Rewards | RewardRecorder | RewardEngineService | Oui, alias vers le singleton existant | ENG-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.
| Provider | Bindings |
|---|---|
ActorServiceProvider | ActorReader → ActeurReadService, ActorWriter → ActeurWriteService |
PublicationServiceProvider | PublicationReader → PublicationReadService, PublicationWriter → PublicationWriteService |
MonetizationServiceProvider | SubscriptionManager → MonetizationWriteService, BoostManager → BoostWriteService, BoostReader → BoostReadService |
TerritoryServiceProvider | TerritoryReader → TerritoryService |
IdentityServiceProvider | IdentityReader → ProfileService |
CommunityServiceProvider | CommunityReader → CommunityReadService |
RewardsModuleServiceProvider | RewardRecorder → RewardEngineService |
Contrats volontairement reportés
| Contrat | Décision | Justification |
|---|---|---|
MunicipalManagementReader/MunicipalManagementWriter | Reporté (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 binding | Reporté | 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écompense | Reporté | 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 globaux | Reporté | ENG-001.2 interdit les contrats métier hors contexte et ne crée pas encore Authorization/Search/Files/Notification. |
| Contrats d'écriture Territory ↔ Municipal | Reporté | 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.
| Sujet | Statut | Constat | Décision à ce stade | Future PR / phase |
|---|---|---|---|---|
| Attribution gratuite de boosts | Besoin 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 Authorization | Design de Platform Service à arbitrer | Les 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 SIRENE | Ownership à arbitrer | CommuneMairieDataRefreshService é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 mairie | Ownership à arbitrer | La 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 Actor | Principe confirmé, signature à définir | Admin é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 |
CommunityWriter | Report 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 |
TerritoryWriter | Bloqué par une décision produit | Un 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
| Commande | Résultat |
|---|---|
php -l sur tous les fichiers PHP créés/modifiés | OK |
php artisan about | OK |
php artisan route:list --except-vendor | OK |
| Bootstrap PHP + résolution container des contrats bindés | OK |
php artisan test --testsuite=Unit | OK : 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és | OK |
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 analyse | Non définie dans composer.json |
vendor/bin/phpstan analyse | vendor/bin/phpstan absent |
Écarts avec la spécification
| Écart | Statut | Justification |
|---|---|---|
| Tous les contrats ne sont pas bindés | Accepté | 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 localement | Bloqué par environnement | L'é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/Territoryrestent 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 :
- utiliser
ActorWriterpour préparer la suppression d'une écriture Admin directe sur Actor ; - ou utiliser
RewardRecordercomme première étape avant les événements Publication/Actor ; - garder
MunicipalManagementReader/MunicipalManagementWriteretPublicationImporterreportés jusqu'à création d'une implémentation dédiée (ENG-001.5 pour Municipal Management).