ADR-057 : Découplage du schéma pivot et extraction vers forge-mvc-pivot¶
Statut¶
Proposé, Forge 1.0.0-rc (ticket ADR-PIVOT-SCHEMA-DECOUPLE-001).
Décision de périmètre cœur/opt-in et de frontière de contrat ; relève du mainteneur.
Date¶
2026-06-29
Contexte¶
Le pivot enrichi (table many_to_many portant des attributs) est un opt-in,
forge-mvc-pivot (ADR-021).
Pourtant, son contrat JSON Schema vit dans le cœur, et il y est entrelacé :
schemas/pivot.schema.json(et sa copiecli/schemas/pivot.schema.json)
décrivent la structure du bloc pivot enrichi ;- surtout,
relations.schema.jsondu cœur référencepivot.schema.json
("$ref": "pivot.schema.json") dans la définitionmanyToMany.
C'est l'inverse de RBAC : rbac.schema.json était autonome (ADR-056), alors que
pivot.schema.json est référencé par un schéma du cœur.
Un simple déplacement du fichier casserait la résolution du schéma relations
du cœur.
Graphe des $ref (vérifié, refs # comprises) :
relations -> pivot, common (entityName, relationName, columnName, onDelete, schemaVersion)
pivot -> field, common (columnName, onDelete, tableName)
field -> common (fieldName)
entity -> field, common
common -> (interne)
relations dépend de pivot, et pivot dépend de field et common.
Constat d'usage : le générateur make:pivot-crud de l'opt-in ne charge pas
pivot.schema.json (il contrôle schema_version à la main).
Le schéma pivot est donc un contrat de documentation / validation IDE, pas un
artefact chargé au runtime du cœur.
Maintenir le contrat d'un opt-in dans le cœur, et coupler un schéma du cœur
(relations) à ce contrat, contredit les principes 4, 8 et 10.
Décision¶
1. Découpler relations (cœur) de pivot (opt-in)¶
Le schéma relations.schema.json du cœur cesse de référencer
pivot.schema.json.
Dans la définition manyToMany, le bloc pivot devient un objet optionnel
opaque : le cœur valide qu'il s'agit d'un objet, sans en valider la structure
interne.
Justification : le many_to_many de base (table de jonction simple) relève
du cœur ; la structure enrichie du pivot (attributs, contraintes) relève de
l'opt-in.
Un projet sans l'opt-in ne déclare pas de bloc pivot enrichi ; un projet avec
l'opt-in obtient la validation stricte de l'opt-in.
Ainsi relations.json reste valide pour le cœur avec ou sans l'opt-in
(entity:validate ne dépend plus du contrat pivot).
2. Extraire pivot.schema.json vers forge-mvc-pivot¶
Le schéma rejoint le paquet (forge_mvc_pivot/schemas/, package-data), où il
valide strictement le bloc pivot enrichi.
Pour être autonome (comme rbac l'est devenu en inlinant schemaVersion,
ADR-056), l'opt-in règle ses dépendances de schéma :
- les définitions
commonutilisées (columnName,onDelete,tableName) sont
inlinées dans le schéma pivot du paquet ; - la référence à
field.schema.jsonest satisfaite en embarquant une copie de
field.schema.json(et decommon.schema.jsonsi nécessaire) aux côtés du
schéma pivot dans le paquet, faute de base de schémas partagée publiée.
Note : la duplication de field/common dans l'opt-in est assumée à ce stade ;
le chantier de déduplication des schémas (source unique) pourra la résorber plus
tard.
3. Retirer le pivot du cœur¶
pivot.schema.json est retiré de cli/schemas/, de schemas/ (racine), du
gabarit cli/skeleton/data/schemas/, et de l'entrée pivot des deux
forge.schema.index.json.
Le jeu de schémas du cœur passe à quatre : common, field, entity,
relations.
Conséquences¶
Le cœur ne porte plus le contrat pivot ni de dépendance vers lui : périmètre
réduit, relations autoporteur.
forge-mvc-pivot devient autoporteur pour son contrat (schéma embarqué et
autonome).
La validation du bloc pivot par le cœur devient permissive (objet opaque) ; la
validation stricte est du ressort de l'opt-in.
Blast radius de l'implémentation (ticket distinct) : retirer le $ref pivot de
relations.schema.json (cœur + copies) et rendre le bloc pivot opaque ; déplacer
pivot.schema.json dans le paquet et le rendre autonome (inline common + copie
field) ; retirer pivot des trois dossiers de schémas et des deux index ; adapter
les tests (test_forge_schema_index, schema:list/schema:doctor 5 -> 4,
test_pivot_json_schema, garde de synchro, wheel) et la documentation.
Alternatives écartées¶
Laisser pivot.schema.json dans le cœur.
Maintient le contrat d'un opt-in dans le cœur et un schéma du cœur couplé à
l'opt-in (principes 4/8/10).
Retirer entièrement le bloc pivot du schéma relations du cœur.
entity:validate (cœur) rejetterait alors un relations.json contenant un bloc
pivot, cassant les projets qui utilisent l'opt-in (la validation des relations
reste une commande du cœur). L'objet opaque préserve la compatibilité.
Publier une base de schémas partagée (field/common) consommable par les
opt-ins.
Plus propre à terme, mais hors périmètre : cela relève du chantier de
déduplication. En attendant, l'opt-in embarque les copies nécessaires.
Charte appliquée¶
- Principe 4 (périmètre du cœur) : le pivot quitte le cœur.
- Principe 8 (noyau minimal, briques opt-in) : l'opt-in porte son contrat.
- Principe 10 (contrat de complétude) :
forge-mvc-pivotfournit son schéma complet. - Règle A (retirer la cause) : on supprime le
$refcœur -> opt-in dansrelations.
Référence¶
- ADR-021 : extraction du pivot enrichi vers forge-mvc-pivot.
- ADR-056 : extraction RBAC (schéma autonome par inline).
- ADR-038 : doc des opt-ins embarquée par paquet.
cli/schemas/relations.schema.json:$refpivot à retirer (rendre le bloc opaque).forge-mvc-pivot/forge_mvc_pivot/make_pivot_crud.py: consommateur du format pivot.