ADR-0017 — Mobilité groupe (étage 2) : Entreprise, ConventionGroupe, mutation dérivée¶
| Statut | ✅ Accepté — 2026-06-17 |
| Portée | Modèle de domaine (Entreprise/Convention), reconstruction des trajectoires, attribution du passif |
| Décision | L'entreprise est une dimension de la photo (pas un flux d'événements) ; la mutation est dérivée par comparaison de photos ; ConventionGroupe ne porte en v1 que la portabilité d'ancienneté ; l'attribution du passif v1 est un breakdown par entreprise |
| Réf. | Vision — étage 2 ; ADR-0005 — Tenant = groupe ; ADR-0016 — Corpus |
🎯 À retenir
L'Entreprise est une dimension de la photo (Salarie.entreprise_id), pas un flux
d'événements : la mutation inter-filiales est dérivée par comparaison de deux photos
(la primitive de l'étage 1, garde-fou 2 honoré). ConventionGroupe ne porte en v1 que
la portabilité d'ancienneté ; l'attribution du passif v1 est un simple breakdown
par entreprise.
Contexte¶
ADR-0005 a tranché l'infrastructure : tenant = groupe, l'Entreprise est une
entité interne, donc une mutation inter-filiales est un événement intra-tenant
(l'isolation 404 reste intacte). La vision (étage 2) décrit pourtant la mutation comme
« une classe d'événements de plus » — mutation(source → cible, date). Or
l'étage 1 a délibérément refusé l'event-sourcing (garde-fou 2) : on reconstruit
depuis des photos datées à matricule stable. Il faut réconcilier les deux.
Décision¶
Quatre partis pris, ratifiés :
- D-MOB-1 — Entreprise = dimension sur la photo (option α, pas de table d'événements).
L'entreprise d'un salarié est portée par l'observation (
Salarie.entreprise_id), pas par un flux d'événements. Une mutation est dérivée : matricule en entreprise X au t₀, Y au t₁ → mutation reconstruite — exactement la primitive de l'étage 1 (transition entre deux observations). On honore le garde-fou 2 ; la physique « force pulsée » reste dans la doc, pas dans le schéma. - D-MOB-2 —
ConventionGroupene porte qu'anciennete_portableen v1. La seule règle actuariellement matérielle aujourd'hui : l'ancienneté se cumule-t-elle au niveau groupe à travers une mutation ? Le reste (reset partiel, conditions de vesting) est différé (YAGNI). - D-MOB-3 — Attribution du passif v1 = breakdown par entreprise. Somme des DBO
par-salarié regroupées par
entreprise_id(réutilise la sortie moteur existante). La méthode profonde (prorata des périodes de service inter-entités, transfert du passif à la mutation) est différée, avec son propre oracle le jour venu. - D-MOB-4 — Confidentialité inter-filiales différée (YAGNI). Conforme à ADR-0005 : on ne construit pas de RBAC scopé entreprise tant qu'un client ne l'exige pas. On préserve l'isolation 404 cross-tenant ; l'entreprise reste une dimension interne au tenant.
- D-MOB-5 — Surface de gestion staff, sans DELETE. L'écran
/groupes(junior + senior, périmètre = celui des trajectoires) crée/édite les conventions et rattache les filiales. Sans lui,anciennete_portablereste à son défaut prudent (portable) et la branche « non portable → reset attendu, silencieux » est inatteignable en prod : cet écran la rend pilotable. Aucun archivage en v1 — une filiale portée par des photos est un fait historique (non supprimable), une convention non rattachée est inerte ; une erreur de saisie se corrige par édition (on évite ainsi d'exposer ces entités dans la corbeille). Les filiales naissent de l'ingestion (get-or-create) ; l'écran ne les crée pas.
Modèle¶
Entreprise:id,tenant_schema(le groupe, indexé),nom,convention_id?,archived_at,created_at. NULL deconvention_id= pas de convention déclarée.ConventionGroupe:id,tenant_schema,nom,anciennete_portable: bool,archived_at,created_at.Salarie.entreprise_id:int | None. NULL = cas dégénéré (groupe mono-entreprise, ou données pré-étage-2) → strictement rétrocompatible.- La photo (
Dossier) reste l'effectif du groupe à une date ; chaque ligne porte sonentreprise_id. Pas d'entreprise sur leDossier.
Conséquences¶
- Reconstruction enrichie (réutilise l'étage 1) : la mutation devient une transition dérivée ; nouvelle anomalie convention-aware — ancienneté ré-initialisée à une mutation alors que la convention la dit portable.
- Le jeu de test multi-entités naît dans le corpus (
validation/datasets/multi-entites/, ADR-0016) avec Green (bouclage / breakdown par entité) et Red (reset d'ancienneté sous convention portable). - Piège prod (db.py) :
Salarie.entreprise_idest une colonne sur une table existante → DOIT entrer dans_COLONNES_AJOUTEES["salarie"], sinon 500 prod.Entreprise/ConventionGroupesont des tables neuves → créées parcreate_all(déjà importées viamodels.py).
🛑 Irréversible
Salarie.entreprise_id s'ajoute à une table existante : sans entrée dans
_COLONNES_AJOUTEES["salarie"] (db.py), la prod (SQLite déployée) renvoie 500.
Les tables neuves Entreprise / ConventionGroupe passent par create_all — pas elles.
Déclencheur de réexamen (pré-enregistré)¶
Si un commissaire aux comptes exige l'attribution fine du passif (prorata des périodes de service par entité, ou transfert à la mutation), ouvrir un ADR dédié avec son oracle — la v1 (breakdown) ne le couvre pas et ne le prétend pas.