Aller au contenu principal

Cartographie des Bounded Contexts réels de DMV Core

Statut

Document d'étude — v1.0 — rédigé le 2026-07-22. Ce n'est pas une ADR. Il applique les principes d'ADR-014 au code existant, dans le but de préparer une future ADR de cartographie, pas de la remplacer. Aucun code n'a été modifié pour produire ce document.

Convention de lecture : chaque affirmation est marquée [Constat] (observé directement dans le code, avec citation), [Interprétation] (lecture DDD d'un constat, discutable), ou [Recommandation] (proposition d'action, non engageante).


1. Méthodologie

L'inventaire s'appuie sur trois lectures indépendantes du dépôt api/ : deux investigations ciblées (Identity/Authorization/Actor/Community/Territory d'une part, Publication/Mairie/ Monetization/notifications d'autre part), chacune avec instruction de ne rapporter que des faits vérifiables avec citation fichier:ligne, et un inventaire complémentaire couvrant les modules restants (AI, Admin, Analytics, AssoSuite, Chat, Import, PlayLoop, Ranking, Rewards, Settings). L'analyse DDD (classification, ownership, recommandations) a été produite après coup, à partir de ces constats — jamais l'inverse, pour éviter de plaquer un modèle théorique sur le dépôt.

Chaque module a été examiné selon quatre angles : ses propres modèles et tables, ses contrôleurs/services, ses imports croisés vers d'autres modules (use App\Modules\...), et ses écritures directes via DB::table() (qui n'apparaissent pas dans un grep d'imports Eloquent et ont nécessité une recherche séparée).

2. Inventaire de l'existant

[Constat] Dix-sept modules existent sous api/app/Modules/ : AI, Actor, Admin, Analytics, AssoSuite, Chat, Community, Identity, Import, Mairie, Monetization, PlayLoop, Publication, Ranking, Rewards, Settings, Territory. C'est plus que ce que documentait le dernier état des lieux (00-governance/01-project-overview.md, 2026-05-08) — AI, Import, Ranking et Rewards sont apparus depuis.

Nombre de routes déclarées par module (indicateur de volume, pas de qualité) :

ModuleRoutesModuleRoutes
Admin135AssoSuite24
Actor40PlayLoop20
Mairie29Community16
Monetization13Publication12
AI12Settings8
Territory8Chat6
Analytics5Identity5
Rewards2

Import et Ranking n'exposent aucune route HTTP propre (confirmé par l'absence de Routes/api.php dans ces deux modules) — ce sont des capacités invoquées par du code serveur (connecteurs d'import, job planifié), pas des points d'entrée API.

3. Analyse par module

Pour chaque module : responsabilités observées, données possédées, et couplages trouvés. Les cinq modules explicitement signalés (Identity, Actor, Community, Mairie, Monetization) sont traités en détail ; les autres reçoivent un traitement plus court, proportionné aux constats trouvés.

Identity

[Constat] Modèles Profile (profiles), UserRole (users_roles). Service AuthController + SupabaseTokenService pour l'échange de jeton (déjà documenté par les travaux précédents). ProfileService::resolveManagedCommunes() (app/Modules/Identity/Services/ProfileService.php:111-164) exécute une jointure directe users_roles → communes → acteurs (lecture seule) et retourne des données de Territory et d'Actor (slug/logo de l'acteur mairie) directement depuis Identity. IdentityServiceProvider délègue la Gate own-actor à Actor\Services\ActorAccessService::isOwner() (Providers/IdentityServiceProvider.php:75-77) — Identity ne décide pas lui-même de l'ownership d'un acteur, il relaie vers Actor.

[Interprétation] Identity reste globalement bien circonscrit à la session et à l'identité brute. resolveManagedCommunes est la seule fuite trouvée, et elle est en lecture seule — c'est un modèle de lecture composé hébergé au mauvais endroit plutôt qu'une violation d'écriture.

Actor

[Constat] Modèle central Acteur (table acteurs), plus ActeurCollaborateur, ActeurTag, ActeurXpEvent, Document, ActeurAlerte (jamais utilisé ailleurs dans le code, voir §6), et surtout ActorAccessService (app/Modules/Actor/Services/ActorAccessService.php), le moteur RBAC central : résout rôle, permissions et modules pour une paire (acteur, utilisateur), avec court-circuit is_platform_admin. Réutilisé tel quel par les deux middleware de Mairie (EnsureMairieAccess.php:60-70, EnsureCommuneManager.php:97-107).

[Constat] Deux écritures croisées trouvées : ActeurModulesController.php:249-284 crée, met à jour et désactive directement des lignes MairieAlerte (table du module Mairie) ; ActeurWriteService.php:122-147 appelle ActorXpService::award() et RewardEngineService::recordEvent() comme effet synchrone de la création d'une publication — en réalité ce dernier point concerne surtout Publication (voir plus bas), mais Actor possède également son propre appel direct à RewardEngineService (ActeurWriteService.php:158,163).

[Interprétation] Actor est correctement propriétaire de ses propres données (acteur, collaborateurs, tags, XP). Le problème n'est pas une mauvaise propriété de données, c'est un rayon d'action trop large côté écriture (Mairie, Rewards) et un rôle d'autorité d'autorisation partiellement centralisé — partiellement seulement, puisque Community, AssoSuite et Monetization ne le réutilisent pas (voir §6).

Community

[Constat] Modèles Contributor, ContributorFavori, Groupe, GroupeMembre, GroupeTag. Tous les imports croisés trouvés (Identity\Models\Profile, Actor\Models\Acteur, Actor\Models\Tag) sont des lectures — relations Eloquent ou validations (Tag::whereIn, CommunityWriteService.php:348). Aucune écriture croisée trouvée. CommunityPolicy implémente sa propre logique d'autorisation (Policies/CommunityPolicy.php:24,64-73), indépendante d'ActorAccessService.

[Interprétation] Community est le module le plus proprement isolé des cinq examinés en détail. Sa seule faiblesse est de participer à la fragmentation de l'autorisation (§6), pas une violation d'ownership de données.

Mairie

[Constat, le plus significatif de cette cartographie] Le module regroupe des responsabilités de nature très différente :

  • Alertes (mairie_alertes) et Services municipaux (mairie_services) : entièrement autonomes dans le module, créés/modifiés uniquement par MairieWriteService.php:34-79 et 101-147, gérés via les tables propres du module. Génériques dans leur mécanique — un acteur de type mairie a des alertes et des services — juste restreints par kind === 'mairie' dans les deux middleware.
  • Publications mairie : délègue entièrement à Publication\Services\PublicationWriteService et PublicationReadService (MairieWriteService.php:12-14,157-212), en forçant scope='mairie' — un bon exemple d'usage de contrat plutôt que de duplication.
  • Champ description/image_url de la commune : écrit directement dans la table communes (propriété de Territory) via DB::table('communes')->update() (MairieWriteService.php:225).
  • Élus, collectes, infos pratiques (commune_elus, commune_collectes, commune_infos) : CRUD complet implémenté dans MairieWriteService.php:233-351 via DB::table() brut, sans passer par les modèles Eloquent Territory\Models\CommuneElu/CommuneCollecte/ CommuneInfo qui existent pourtant pour ces mêmes tables. Les lectures, elles, utilisent bien ces modèles Territory (MairieReadService.php:13-16,100-124). Territory possède, de son côté, un troisième chemin d'écriture indépendant sur ces mêmes tables : le rafraîchissement automatique SIRENE (Territory/Services/CommuneMairieDataRefreshService.php:218,232,246,260,314,321,323).

Trois chemins de code distincts, dans deux modules différents, écrivent aujourd'hui dans les mêmes trois tables.

[Interprétation] « Mairie » n'est pas aujourd'hui un Bounded Context cohérent — c'est un assemblage de deux natures de responsabilité bien distinctes : (a) des capacités acteur-génériques restreintes à un type d'acteur (Alertes, Services, Publications), qui relèvent conceptuellement d'Actor ; (b) de la gestion administrative d'une commune (Élus, Collectes, Infos, champs de la commune elle-même), qui relève conceptuellement de Territory, mais dont le code d'écriture vit aujourd'hui dans Mairie plutôt que dans Territory. C'est précisément le cas que la mission demandait de ne pas trancher par réflexe — voir §5 pour le développement complet.

Monetization

[Constat] Modèles propres (ActorSubscription, Boost, BoostCatalog, BoostPurchase, PushUsage, SubscriptionLog, SubscriptionPlan) tous correctement scopés. Mais plusieurs écritures directes dans la table acteurs (propriété d'Actor) : featured_until (BoostUsageService.php:207-212), stripe_account_id (MonetizationWriteService.php:62-63,362-366), badge_verifie/badge_label (MonetizationWriteService.php:392-412, docblock indiquant qu'il s'agit de la ré-implémentation d'un trigger Supabase existant). L'autorisation d'accès à un acteur est réimplémentée indépendamment (ensureOwner() dans trois contrôleurs, requête directe sur user_acteurs) plutôt que de réutiliser ActorAccessService.

[Interprétation] Monetization connaît trop peu Actor pour ses propres besoins (aucune lecture des permissions existantes) et le connaît trop pour ce qui devrait rester du ressort d'Actor (badge, mise en avant) — les deux symptômes viennent du même défaut : aucun contrat publié entre les deux modules, seulement des accès directs dans les deux sens.

Publication

[Constat] Modèles propres et cohérents (Publication, PublicationCommune, PublicationReport, TypePublication). Aucune logique de notification ou d'email trouvée. En revanche, PublicationWriteService.php:122-147 appelle directement ActorXpService::award() et RewardEngineService::recordEvent() à trois reprises comme conséquence synchrone de la création d'une publication.

[Interprétation] C'est la violation la plus nette et la plus directement comparable à l'exemple donné par ADR-014 lui-même (« Publications ne doit pas contenir de logique de notifications ») — ici appliqué à l'XP et aux récompenses plutôt qu'aux notifications, mais de nature strictement identique : Publication décide d'une conséquence externe à sa propre responsabilité.

Territory

[Constat] Modèles Commune, CommuneCollecte, CommuneElu, CommuneInfo, CommuneDefibrillateur(Sync), CommuneSireneSnapshot, DefibrillateurCatalogue(Sync). La colonne communes.mairie_actor_id (0000_00_00_000005_create_communes_table.php:32, re-confirmée 000073_add_mairie_actor_id_to_communes.php:28) n'a aucune contrainte de clé étrangère en base — un UUID indexé nullable, sans garantie référentielle. Fait plus significatif : CommuneMairieDataRefreshService.php:148,158 écrit directement dans la table acteurs (kind, categorie_id, numero_siret) via DB::table('acteurs')->update() — une écriture croisée dans le sens inverse de celle observée côté Mairie (Territory → Actor cette fois).

[Interprétation] Territory n'est pas seulement consulté par Mairie, il écrit lui-même dans Actor. Le couplage Actor ↔ Territory ↔ Mairie est bidirectionnel et déjà profond, confirmant que ces trois modules ne peuvent pas être analysés indépendamment (voir §5).

AI

[Constat] Prompts, providers, cache, quotas, logs d'usage — aucune donnée métier propre, aucun couplage à un autre module trouvé dans l'inventaire (non vérifié aussi exhaustivement que les cinq modules prioritaires).

[Interprétation] Ne ressemble pas à un Bounded Context métier — c'est une capacité technique transverse, cohérente avec le vocabulaire déjà établi (Knowledge Engine / Experience Engine, 08-frontend/architecture DMV synthese.md §7.4) plutôt qu'un domaine au sens de cette cartographie.

Admin

[Constat] 24 contrôleurs/services, 135 routes, 17 imports croisés recensés par l'agent d'investigation vers Actor, Territory, Identity, Monetization, Publication — la totalité des modules métier examinés. Sert exclusivement le Backoffice.

[Interprétation] Ressemble à un adaptateur au sens d'ADR-014 §12 (traduction vers un consommateur externe, agrégation en lecture) plutôt qu'à un Bounded Context — mais ceci n'a pas été vérifié aussi rigoureusement que les cinq modules prioritaires : je n'ai pas contrôlé si ses services d'écriture (AdminActeurService, AdminClaimService, etc.) dupliquent des règles métier plutôt que de déléguer aux modules propriétaires. Point à vérifier avant toute conclusion définitive.

Analytics

[Constat] Docblock explicite : « Aucune écriture, aucun recalcul dans PHP » (AnalyticsService.php:18) — s'appuie sur des fonctions PostgreSQL (DB::select('SELECT * FROM get_stats_acteur(...)')). Lit Monetization\Models\PushUsage.

[Interprétation] Un modèle de lecture composé au sens propre du terme — exactement le même schéma que Search dans l'étude précédente sur DMV Core.

AssoSuite, PlayLoop

[Constat] AssoSuite possède ses propres modèles (AssoMember, AssoProject, AssoCotisation...) et son propre middleware d'autorisation (EnsureAssoAccess.php:24-31, vérifie directement AssoMember sans passer par ActorAccessService). PlayLoop possède également ses propres modèles et son propre mécanisme d'authentification par appareil (DeviceTokenMiddleware).

[Interprétation] Les deux sont déjà des Bounded Contexts correctement isolés — cohérent avec ADR-000, qui prévoit qu'ils deviennent des applications à part entière. Leur seul défaut commun est de réimplémenter leur propre logique d'autorisation plutôt que de converger vers un mécanisme partagé (§6).

Chat, Settings, Ranking, Rewards, Import

[Constat] Chat ne référence que Identity\Models\Profile — aucun couplage à Actor ou Community trouvé. Settings ne présente aucun import croisé côté écriture. Ranking lit app_settings (table de Settings) via DB::table() brut plutôt que via le service SettingsReadService (RankingService.php:53). Rewards est invoqué directement et synchronement par Actor et Publication (voir plus haut) plutôt que de réagir à des événements. Import contient des connecteurs (BessanFrConnector...) qui font clairement office de couche anti-corruption pour des données externes, sans exposer de route HTTP propre.

[Interprétation] Chat est un candidat à trancher entre Bounded Context autonome (« Messaging ») ou Platform Service générique — la question reste ouverte (§8). Ranking ressemble à un composant interne de l'Experience Engine plutôt qu'à un Bounded Context de plein droit. Import est un adaptateur, pas un Bounded Context.

4. Bounded Contexts proposés

RegroupementClassification proposéeConfiance
IdentityBounded Context, périmètre déjà correct (hors resolveManagedCommunes)Élevé
ActorBounded Context, ownership de données correct, rayon d'écriture à corrigerÉlevé
CommunityBounded Context, déjà propreÉlevé
PublicationBounded Context, ownership correct, une violation ponctuelle à corrigerÉlevé
MonetizationBounded Context, ownership correct, écritures croisées à corrigerÉlevé
AssoSuiteBounded Context déjà isolé, candidat à extraction future (cohérent ADR-000)Élevé
PlayLoopBounded Context déjà isolé, candidat à extraction future (cohérent ADR-000)Élevé (non vérifié aussi exhaustivement)
RewardsBounded Context valide, mais mal invoqué (synchrone plutôt qu'événementiel)Élevé
TerritoryBounded Context, mais périmètre exact à redéfinir avec Mairie (§5)Moyen
MairiePas un Bounded Context cohérent en l'état — à scinder (§5)Moyen
AuthorizationN'existe pas encore comme Bounded Context/capacité propre — actuellement fragmenté dans Actor, Community, AssoSuite, MonetizationFaible (nécessite une décision, pas une simple observation)
ClaimsOwnership ambigu aujourd'hui (soumission dans Actor, traitement dans Admin) — candidat à devenir son propre petit contexteFaible
AI, Analytics, SettingsPlatform Services / capacités transverses, pas des Bounded Contexts métierÉlevé
ImportAdaptateur (anti-corruption layer), pas un Bounded ContextMoyen
RankingProbable composant interne de l'Experience Engine, pas un Bounded Context autonomeFaible (peu vérifié)
ChatBounded Context autonome ou Platform Service générique — non tranchéFaible
AdminAdaptateur pour le Backoffice, pas un Bounded Context — écritures non auditéesMoyen

5. Ownership et contrats — le cas Actor / Mairie / Territory en détail

[Interprétation, développée] La preuve du code converge vers trois masses distinctes, actuellement mélangées dans le seul module Mairie :

Ce qui appartient à Actor : Alertes, Services municipaux, et le fait qu'une publication soit scope='mairie'. Rien de spécifique à la gestion d'une commune ici — c'est un acteur, de type mairie, qui utilise des capacités déjà génériques (alertes, services, publications) avec une restriction de type. [Recommandation] Ces trois capacités devraient être exposées comme des extensions du contrat d'Actor (ou rester dans un sous-module Mairie qui ne fait qu'orchestrer des appels à Actor et Publication), jamais comme un domaine métier séparé.

Ce qui appartient à Territory : Élus, Collectes, Infos pratiques, et les champs propres de la commune (description, image). Ce sont des données sur la commune en tant qu'entité administrative — leur pertinence ne dépend pas de l'existence d'un acteur mairie. Aujourd'hui leur écriture vit dans Mairie via SQL brut, en cour-circuitant les modèles Eloquent que Territory possède déjà pour ces mêmes tables. [Recommandation] Cette écriture devrait migrer vers Territory (ou un contexte de gestion municipale distinct, voir ci-dessous), exposée comme un contrat que Mairie (ou la future application MairieSuite) invoque, plutôt que par accès direct aux tables.

Ce qui pourrait relever d'un contexte "Municipal Management" distinct : la question n'est pas définitivement tranchée par le code. Territory, tel qu'il existe aujourd'hui, mélange deux préoccupations différentes — des données de référence publique (communes, défibrillateurs, SIRENE) alimentées par des connecteurs automatisés, et une gestion administrative pilotée par un utilisateur autorisé (municipal_manager, vérifié par EnsureCommuneManager). [Interprétation, confiance faible] Un contexte « Municipal Management », séparé de Territory (référence publique) et distinct d'Actor (identité de l'acteur mairie), est défendable par analogie avec Publication (éditorial) séparé d'Actor (identité) — mais rien dans le code n'impose ce choix plutôt que de simplement transférer Élus/Collectes/Infos à Territory tel quel. C'est une décision produit, pas une déduction du code (voir §8).

Ce qui relève d'une future application cliente (MairieSuite) : rien dans DMV Core lui-même — ADR-000 a déjà tranché que MairieSuite sera une application, pas un prolongement de Workspace. Le périmètre exact de ce qu'elle consommera dépend directement de l'issue de la question précédente.

Ce qui devrait sortir de DMV Core : rien d'identifié dans le module Mairie qui ne corresponde à aucune des trois catégories ci-dessus — pas de constat allant dans ce sens.

6. Violations ou ambiguïtés observées

SituationFichier(s)Classification
Publication décide d'attribuer XP et récompensesPublicationWriteService.php:122-147Violation directe d'ADR-014 (principe 2/4)
Actor écrit directement dans MairieAlerteActeurModulesController.php:249-284Violation directe — capacité mal placée, à repositionner dans Mairie
Actor invoque directement RewardsActeurWriteService.php:158,163Violation directe (même nature que Publication)
Territory écrit directement dans acteurs (SQL brut)CommuneMairieDataRefreshService.php:148,158Violation directe — écriture croisée, contourne le modèle Acteur
Mairie écrit dans communes, commune_elus, commune_collectes, commune_infos en SQL brut, en parallèle des modèles Eloquent TerritoryMairieWriteService.php:225,233-351Violation directe + ambiguïté de fond (§5) — deux chemins d'écriture concurrents sur les mêmes tables
Monetization écrit featured_until, stripe_account_id, badge_verifie/label dans acteursBoostUsageService.php:207-212, MonetizationWriteService.php:62-63,362-366,392-412Violation directe
Quatre implémentations indépendantes d'autorisation acteur-scopée (ActorAccessService, CommunityPolicy, EnsureAssoAccess, ensureOwner() de Monetization)Actor, Community, AssoSuite, MonetizationAmbiguïté architecturale — pas une violation d'ADR-014 au sens strict (aucune n'écrit dans les données d'un autre), mais une absence de contrat partagé qui les expose à diverger silencieusement
resolveManagedCommunes compose des données Territory/Actor à l'intérieur d'IdentityProfileService.php:111-164Dette mineure — lecture seule, à faire évoluer vers un modèle de lecture composé explicite plutôt qu'un correctif
acteur_alertes : aucun point d'appel trouvé dans tout le code PHPActeurAlerte.php (modèle seul)Ni une violation ni une dette — probablement une fonctionnalité inachevée ou orpheline, à vérifier avec l'équipe produit
Ownership de claims non clarifié (soumission dans Actor, traitement dans Admin, aucun modèle Claim dédié)ActeurWriteService.php (soumission), AdminClaimController/Service (traitement)Ambiguïté nécessitant une décision produit
RankingService lit app_settings en SQL brut plutôt que via le service de SettingsRankingService.php:53Dette mineure, lecture seule, faible risque

Aucune situation classée « acceptable temporairement » au sens strict — chaque écriture croisée trouvée touche une donnée qui a un propriétaire clair ailleurs dans le code (Actor, Territory), ce qui en fait des violations directes plutôt que des zones grises.

7. Carte cible

Carte proposée à l'issue de l'analyse — les frontières en confiance faible ou moyenne sont signalées comme telles, pas présentées comme acquises.

Identity [confiance : élevé]

├── possède : session, profil, rôles bruts
└── publie : UserAuthenticated, RoleAssigned

Authorization (capacité à extraire) [confiance : faible — décision requise]

├── possède : le mécanisme générique de résolution rôle/permission
└── consommé par : Actor, Community, AssoSuite, Monetization (aujourd'hui dupliqué
indépendamment par chacun)

Actor [confiance : élevé]

├── possède : identité organisationnelle, collaborateurs, tags, XP, badges
├── consomme : Authorization (lecture), Territory (lecture, commune de rattachement)
└── publie : ActorCreated, ActorUpdated, ActorBadgeEligible (fait, pas décision — voir
Monetization ci-dessous)

Territory [confiance : moyen]

├── possède : communes (données de référence publique), élus, collectes, infos
│ pratiques — périmètre à confirmer avec Municipal Management (§5, §8)
└── publie : CommuneUpdated

Municipal Management (candidat, non tranché) [confiance : faible — décision requise]

└── gestion administrative d'une commune par un utilisateur autorisé — peut rester
fusionné avec Territory selon l'arbitrage produit du §5

Community [confiance : élevé]

├── possède : contributeurs, groupes, favoris
└── consomme : Identity, Actor (lecture seule)

Publication [confiance : élevé]

├── possède : cycle de vie éditorial, modération, reports
├── référence : acteur_id
└── publie : PublicationPublished, PublicationModerated (au lieu d'appeler XP/Rewards
directement)

Monetization [confiance : élevé]

├── possède : plans, abonnements, boosts, facturation Stripe
├── référence : acteur_id
└── publie : BoostPurchased, SubscriptionChanged, StripeAccountUpdated (au lieu d'écrire
directement dans acteurs)

Rewards [confiance : élevé]

├── possède : règles de récompense, octrois, XP
└── consomme (événements, pas appel direct) : PublicationPublished, BoostPurchased

AssoSuite, PlayLoop [confiance : élevé]

└── déjà isolés — candidats naturels à devenir des applications (ADR-000)

Claims (candidat, non tranché) [confiance : faible — décision requise]

└── workflow de revendication d'une fiche acteur — aujourd'hui dispersé entre Actor et
Admin

AI, Analytics, Settings, Import [confiance : élevé]

└── Platform Services / adaptateurs, pas des Bounded Contexts métier

Admin [confiance : moyen]

└── adaptateur du Backoffice — écritures propres non encore auditées

8. Points restant à arbitrer

Cinq décisions ne peuvent pas être prises par la seule lecture du code — elles engagent le produit, pas seulement l'architecture :

  1. Le périmètre Territory / Municipal Management. Élus, Collectes, Infos doivent-ils rejoindre Territory tel quel, ou justifient-ils un contexte de gestion municipale distinct ? Détermine directement le périmètre de la future application MairieSuite.
  2. L'extraction d'un contexte Authorization. Les quatre implémentations actuelles doivent-elles converger vers une seule capacité partagée, et si oui, portée par quel module — un nouveau contexte dédié, ou une promotion d'ActorAccessService au rang de Platform Service généralisé ?
  3. Le statut de Claims. Contexte à part entière avec son propre cycle de vie (soumission → revue → décision), ou sous-domaine d'Actor traité par Admin comme aujourd'hui ?
  4. Le statut de Chat. Bounded Context métier (« Messaging »formant) ou Platform Service générique de messagerie réutilisable par plusieurs contextes ?
  5. acteur_alertes. Fonctionnalité abandonnée à retirer, ou fonctionnalité prévue mais jamais reliée à un point d'entrée — à vérifier avec l'équipe produit avant toute décision de conservation ou de suppression.

9. Recommandations pour l'étape suivante

[Recommandation] Ne pas traiter cette cartographie comme suffisante pour lancer une migration — c'est un état des lieux, pas un plan. Trois actions suggérées avant toute ADR de migration :

  1. Trancher les cinq points du §8 avec l'équipe produit — ce sont des décisions, pas des approfondissements techniques supplémentaires à mener seul.
  2. Auditer les services d'écriture d'Admin (non fait ici, signalé comme lacune au §3) avant de le classer définitivement comme adaptateur pur.
  3. Prioriser les corrections des violations du §6 par risque plutôt que par ordre d'apparition — les écritures croisées touchant acteurs (Territory, Monetization) et commune_elus/collectes/infos (Mairie) sont les plus profondément ancrées et les plus coûteuses à corriger plus tard ; les appels directs à Rewards/XP (Actor, Publication) sont plus superficiels et plus rapides à convertir en événements.

Aucun plan de migration détaillé n'est proposé ici, conformément au périmètre de cette mission.

Liens associés

  • docs/decisions/ADR-000-dmv-core-modular-applications.md
  • docs/decisions/ADR-011-wall-engine-context-engine.md
  • docs/decisions/ADR-012-architecture-foundation-frozen.md
  • docs/decisions/ADR-013-workspace-as-application.md
  • docs/decisions/ADR-014-dmv-core-bounded-contexts.md
  • docs/06-architecture/26-platform-architecture.md
  • docs/06-architecture/27-architecture-governance.md