Aller au contenu principal

Modèle de données

LeBoard partage la même base PostgreSQL que LeHub. Les modèles de Board ne touchent jamais aux entités du Hub (Workshop, Registration, Member, etc.), sauf pour la lecture de WorkshopModel (catalogue des plateaux) et Member (authentification animateur).

Le schéma Prisma de LeBoard vit dans son propre prisma/schema.prisma : c'est un miroir partiel du schéma du Hub, limité aux tables qu'il utilise. Les migrations propres à LeBoard sont versionnées dans son dépôt et appliquées à son déploiement.

Les quatorze modèles déclarés côté LeBoard : Board, CardPlacement, Connection, AnimatorSession, StickyNote, BoardArrow, WorkshopModel, ModelCardTitle, Lot, BoardLotDistribution, Member, et les trois tables de la Régénération (RegenerationFamily, RegenerationPractice, RegenerationExample).

Prisma 7 des deux côtés

Depuis octobre 2026, LeBoard est aussi sur Prisma 7 : client généré dans generated/prisma (hors git), connexion par @prisma/adapter-pg, URL de connexion dans prisma.config.ts (CLI) et src/lib/prisma.ts (adaptateur) — plus dans le bloc datasource. Les deux applications doivent rester sur la même version majeure, puisqu'elles parlent à la même base.

Modèles Board​

Board​

Représente un plateau collaboratif (une instance par atelier en ligne).

ChampTypeRôle
idString (CUID)Identifiant unique
workshopIdString (unique)Référence atelier LeHub (1-to-1)
workshopTitleStringTitre atelier copié (pour affichage sans req LeHub)
tokenString (unique)Token d'accès public (URL : /b/[token])
languageString"fr" (défaut) / "en" / "es" — langue des cartes
statusString"active" (défaut) / "closed"
isTrainingBooleanAtelier de test (formation) — affiche badge dans header
expiresAtDateTime?Expiration board (optionnel, utilisé pour les ateliers test)
modelIdString?Référence WorkshopModel (Hub) — définit les étapes/lots/matrices
placementsCardPlacement[]Cartes posées sur le board
connectionsConnection[]Flèches entre cartes
lotDistributionsBoardLotDistribution[]Historique distributions des lots
animatorSessionsAnimatorSession[]Sessions animateur actives
stickyNotesStickyNote[]Post-its
arrowsBoardArrow[]Flèches avec anchorpoints
activeStageIdStringÉtape active (persistée pour survivre aux déploiements)
createdAt, updatedAtDateTimeTimestamps

Relations : WorkshopModel (optionnel, peut être null si modèle supprimé), CardPlacement (cascade delete), Connection (cascade delete), StickyNote (cascade delete), BoardArrow (cascade delete), BoardLotDistribution (cascade delete), AnimatorSession (cascade delete).

CardPlacement​

Représente la position d'une carte sur le plateau (placement temporaire).

ChampTypeRôle
idString (CUID)Identifiant unique
boardIdStringBoard (FK)
cardIdStringID métier de la carte (ex: "card-001") — pas une FK, juste une référence textuelle
x, yFloatPositionnement en pixels
rotationFloatRotation en degrés (défaut 0)
flippedBooleanRecto/verso (défaut false = recto)
zIndexIntOrdre de rendu (défaut 0)
fromConnectionsConnection[]Flèches partant de cette carte
toConnectionsConnection[]Flèches vers cette carte
updatedAtDateTimeTimestamp dernière modification

Cascade delete sur Board : si board est supprimé, toutes ses CardPlacements disparaissent.

Connection​

Représente une flèche de causalité entre deux cartes.

ChampTypeRôle
idString (CUID)Identifiant unique
boardIdStringBoard (FK)
fromId, toIdStringCardPlacement IDs (FK) — relation CardPlacement.fromConnections / toConnections
labelString?Texte optionnel sur la flèche
createdAtDateTimeTimestamp création

Cascade delete sur fromId/toId : si une CardPlacement est supprimée, ses flèches disparaissent aussi.

BoardLotDistribution​

Représente une distribution (historique) d'un lot à un board.

ChampTypeRôle
idString (CUID)Identifiant unique
boardIdStringBoard (FK)
lotIdStringLot ID (FK du Hub)
distributedAtDateTimeTimestamp distribution
modeString"distribute" ou "popcorn" — mode de lancement choisi par l'animateur
Unique[boardId, lotId]Un seul record par (board, lot) pair — prevent doublons

Piège : suppression en cascade — si un Lot est supprimé du Hub, les BoardLotDistribution liés disparaissent en cascade. Cela ne pose pas de problème en prod (les lots ne sont jamais supprimés, juste archivés), mais en dev peut causer des surprises. Voir la note de la tâche 3 pour plus de détails.

Cascade delete sur Board : si board supprimé, toutes ses distributions disparaissent.

StickyNote​

Représente un post-it libre sur le plateau.

ChampTypeRôle
idStringID custom (UUID v4) — pas CUID, pour contourner un bug Konva
boardIdStringBoard (FK)
textStringContenu texte (défaut "")
x, yFloatPosition en pixels
colorString"yellow" / "pink" / "blue" / "green" / "white" (défaut "yellow")
zIndexIntOrdre rendu (défaut 0)
width, heightFloatDimensions en pixels (défaut 180×150)
fontSizeIntTaille police (défaut 12)
createdAt, updatedAtDateTimeTimestamps

Cascade delete sur Board.

BoardArrow​

Représente une flèche entre anchorpoints (variante plus riche que Connection).

ChampTypeRôle
idString (CUID)Identifiant unique
boardIdStringBoard (FK)
fromType, toTypeString"card" / "sticky" — type source/cible
fromId, toIdStringID carte ou post-it
fromAnchor, toAnchorStringAnchorpoint ("tl", "tc", "tr", "ml", "mr", "bl", "bc", "br")
directionString"forward" / "both" (défaut "forward")
colorStringCouleur flèche (défaut "gray")
strokeWidthIntÉpaisseur trait (défaut 2)
createdAt, updatedAtDateTimeTimestamps

Cascade delete sur Board.

AnimatorSession​

Représente une session animateur active (pour tracking connexion).

ChampTypeRôle
idString (CUID)Identifiant unique
boardIdStringBoard (FK)
memberIdStringID animateur (Hub Member)
pseudoStringPseudo affiché (défaut "")
expiresAtDateTimeExpiration session (défaut +24h)
createdAtDateTimeTimestamp création

Cascade delete sur Board. Utilisé pour tracer les connexions animateur et implémenter le timeout (logout après 24h ou fermeture browser).

Modèles de référence (LeHub, lecture seule)​

WorkshopModel​

Catalogue des modèles d'atelier (étapes, lots, matrices). Créé et géré exclusivement par LeHub via /admin/cards/.

ChampTypeRôle
idString (CUID)Identifiant unique
nameStringNom du modèle (ex: "Fresque Systémique v1")
languageString"fr" (défaut)
plateauIdString?ID plateau (maps à src/lib/plateaux.ts)
lotsLot[]Les lots du modèle
boardsBoard[]Les boards utilisant ce modèle
createdAt, updatedAtDateTimeTimestamps

Lot​

Ensemble de cartes ou diapositives à lancer ensemble. Créé via LeHub.

ChampTypeRôle
idString (CUID)Identifiant unique
modelIdStringWorkshopModel (FK)
nameStringNom du lot (ex: "Actions")
orderIntPosition dans la séquence
stageIdString?Étape liée (ex: "stage-2")
kindString?"normal" (défaut) / "emergence" / null
cardIdsStringJSON stringifié : [{"cardId":"card-001","col":0,"row":0},...]
contentTypeString"cards" (défaut) / "slides"
slidesDataString?JSON stringifié si diaporama (images + notes)
distributionsBoardLotDistribution[]Historique distributions pour ce lot

Cascade delete si WorkshopModel supprimé.

Member​

Utilisateur du Hub (animateurs, admins). Lecture seule pour LeBoard.

Champ clésTypeRôle
idStringID unique
emailStringEmail
roleStringRôle pédagogique ("animateur", "formateur", etc.)
isAdminBooleanAccès admin

Utilisé uniquement pour valider le JWT de l'animateur et tracer les sessions.

RegenerationFamily, RegenerationPractice, RegenerationExample​

Miroir des trois tables du Hub, en lecture seule pour LeBoard. Elles portent le contenu du feuillet « Régénération » : des familles de pratiques, une pratique par carte, et les exemples rattachés (83 au moment de la reprise du contenu). Tout est scopé à une version de cartes (workshopModelId), et c'est le Hub qui les édite.

LeBoard les lit par GET /api/regeneration?workshopModelId=…, derrière le token du plateau.

Ce contenu est du matériel pédagogique de l'association

La route servait autrefois ce contenu à qui le demandait : il suffisait d'un workshopModelId, que le Hub publiait alors sans authentification. Les deux bouts de la chaîne ont été fermés lors de l'audit du 20 septembre 2026. Le laissez-passer est désormais le token d'un plateau qui utilise ce modèle — ce qui laisse l'accès à l'animateur comme aux participants pendant l'atelier, sans inventer de session.

Diagramme relations Board​

WorkshopModel (Hub admin)
│
├─ plateauId (ref → src/lib/plateaux.ts)
└─ Lot[] (lots du modèle)
│
└─ BoardLotDistribution (historique distribution)
│
└─ Board (plusieurs boards par lot)
├─ CardPlacement[] (cartes posées)
│ └─ Connection[] (flèches vers d'autres cartes)
├─ StickyNote[] (post-its)
├─ BoardArrow[] (flèches avancées)
├─ AnimatorSession[] (sessions animateurs)
└─ activeStageId (étape actuelle)

Member (Hub auth)
│
└─ AnimatorSession (liaison animateur → board)

Remarques opérationnelles​

  • Pas de soft delete sur Board et dérivés (contrairement au Hub). Les boards sont créés à la volée et expirés via expiresAt.
  • Pas de relation directe Workshop : Board référence juste workshopId textuel, pas une FK vers Workshop. Cela permet une indépendance légère (Board peut exister si Workshop est supprimé).
  • Partage DB : toute migration Board doit être versionnée dans ce dépôt (prisma/migrations/) et appliquée à son déploiement. Une table partagée ne doit jamais être modifiée des deux côtés dans la même fenêtre : c'est la même base.
  • Le miroir n'est pas une copie complète : une colonne ajoutée côté Hub sur une table partagée reste invisible de LeBoard jusqu'à ce que son schéma la déclare aussi. L'inverse est plus gênant : une colonne ajoutée par LeBoard et absente du schéma du Hub finit par être « rattrapée » par une migration du Hub qui ne la connaît pas.