Aller au contenu

Schéma de la base de données

La base MySQL 8 de PerfShop est gérée par Flyway. Les migrations sont situées dans backend/src/main/resources/db/migration-fr/ et sont versionnées de V1 à V10. Le choix du dossier migration-fr au lieu de migration est délibéré : c'est un schéma consolidé dans son état final, sans historique d'ALTER TABLE. Cette approche facilite la lecture pédagogique (un prof peut comprendre le schéma en lisant un seul fichier) et accélère les déploiements propres.

Sources

backend/src/main/resources/db/migration-fr/V1__schema.sql à V10__data_pedagogique.sql

Vue d'ensemble

Migration Taille Rôle
V1__schema.sql 13 KB Création de toutes les tables dans leur état final
V2__data_users.sql 3 KB Comptes utilisateurs initiaux (agents de démo, comptes de test)
V3__data_informatique.sql 44 KB Seed catalogue — catégorie Informatique (~200 produits)
V4__data_avions.sql 46 KB Seed catalogue — catégorie Avions
V5__data_voitures.sql 46 KB Seed catalogue — catégorie Voitures
V6__data_entreprises.sql 65 KB Seed catalogue — catégorie Entreprises (valorisations)
V7__data_helicopteres.sql 29 KB Seed catalogue — catégorie Hélicoptères
V8__data_bateaux.sql 8 KB Seed catalogue — catégorie Bateaux
V9__data_motos.sql 7 KB Seed catalogue — catégorie Motos
V10__data_pedagogique.sql 7 KB Infrastructure pédagogique : 4 produits + 5 agents BAC1-BAC5

Le catalogue complet totalise environ 1 000 produits répartis sur sept catégories hétéroclites. Cette variété est voulue : elle permet aux énigmes pédagogiques d'exploiter des critères de recherche distinctifs et aux tests de charge de simuler une volumétrie réaliste.

Diagramme entité-relation

erDiagram
  users ||--o{ cart_items    : "possède"
  users ||--o{ orders        : "passe"
  products ||--o{ cart_items : "référencé par"
  products ||--o{ order_items: "référencé par"
  orders ||--o{ order_items  : "contient"

  users {
    BIGINT id PK
    VARCHAR email UK
    VARCHAR password "BCrypt $2b$10$"
    VARCHAR first_name
    VARCHAR last_name
    VARCHAR civility "M | Mme | Mx"
    DATE birth_date
    VARCHAR phone
    TEXT street
    VARCHAR postal_code "VARCHAR(5) — cible Chaos Pédagogique N2+"
    VARCHAR city
    VARCHAR region
    VARCHAR country "ISO2, default FR"
    DATETIME created_at
    DATETIME last_login
    BOOLEAN is_pedagogique "agents invisibles IHM"
  }

  products {
    BIGINT id PK
    VARCHAR name
    VARCHAR description
    DECIMAL price "DECIMAL(18,2)"
    INT stock
    VARCHAR category
    VARCHAR image_url
    DATETIME created_at
    DATETIME updated_at
    BOOLEAN is_pedagogique "produits invisibles IHM"
    BOOLEAN pedagogique_protected "stock non décrémenté"
  }

  cart_items {
    BIGINT id PK
    BIGINT user_id FK
    VARCHAR session_id "panier anonyme"
    BIGINT product_id FK
    INT quantity
    DECIMAL price
    DATETIME added_at
  }

  orders {
    BIGINT id PK
    VARCHAR order_number UK
    BIGINT user_id FK
    DECIMAL total_amount
    ENUM status "PENDING..CANCELLED"
    VARCHAR shipping_address
    VARCHAR shipping_method
    VARCHAR payment_method
    VARCHAR card_last4 "jamais le CVV"
    TINYINT cvv_verified
    BOOLEAN is_test_data "commandes agents exclues"
    DATETIME created_at
    DATETIME updated_at
  }

  order_items {
    BIGINT id PK
    BIGINT order_id FK
    BIGINT product_id FK
    INT quantity
    DECIMAL unit_price
    DECIMAL subtotal
  }

  admin_users {
    BIGINT id PK
    VARCHAR email UK
    VARCHAR password_hash "BCrypt"
    BOOLEAN can_access_chaos
    BOOLEAN can_access_monitoring
    BOOLEAN can_access_admin
    BOOLEAN can_access_jmeter
    BOOLEAN can_access_scripts
    BOOLEAN is_superadmin
    DATETIME created_at
  }

  perfshop_license {
    VARCHAR id PK "toujours current"
    TEXT license_key
    VARCHAR plan "functional | performance | enterprise"
    VARCHAR holder
    DATE issued_at
    DATE expires_at "NULL = illimitée"
    DATETIME activated_at
    TINYINT revoked
  }

  pedagogique_sessions {
    VARCHAR token PK "UUID"
    VARCHAR alias
    VARCHAR level "bac1..bac5"
    BIGINT joined_at "epoch ms"
    BIGINT completed_at
    INT current_step
    VARCHAR agent_code
    VARCHAR extraction_answer_hash
    VARCHAR logique_question_indices
    VARCHAR logique_expected_hash
    TEXT attempts_json
    TIMESTAMP created_at
  }

Les tables admin_users, perfshop_license et pedagogique_sessions sont volontairement détachées du graphe principal — elles gèrent des aspects transverses (authentification formateur, licence, parcours pédagogique) qui ne s'intègrent pas dans le modèle e-commerce.

V1 — Schéma complet

V1__schema.sql crée toutes les tables dans leur état final. Aucun ALTER TABLE n'est exécuté après. Cette consolidation est une réécriture délibérée du schéma historique (qui comportait 38 migrations incrémentales) pour offrir aux étudiants une lecture plus claire.

Table users

Identifie les clients du shop. Contient les champs classiques (email, password BCrypt, nom, prénom) enrichis par les extensions du Chaos Métier :

  • civility : VARCHAR(5) et non ENUM — Hibernate valide le schéma contre VARCHAR. La contrainte métier (M, Mme, Mx) est gérée côté application par ValidationService.
  • birth_date : date de naissance, utilisée par le Chaos Métier pour des règles comme la remise fidélité.
  • Adresse décomposée : street, postal_code, city, region, country (au lieu d'un seul champ address). Cette décomposition est exigée par plusieurs chaos métier.
  • postal_code limité à 5 caractères : limite intentionnelle. Le Chaos Pédagogique Niveau 2+ tente volontairement d'insérer une valeur plus longue, ce qui déclenche une DataTruncation MySQL observable dans les logs et les métriques — c'est une démonstration pédagogique de « bug silencieux sous chaos ».
  • country : ISO2, défaut FR.
  • is_pedagogique : drapeau qui identifie les comptes « agents » créés par V10. Ces comptes sont invisibles dans l'IHM admin et leurs commandes sont exclues des statistiques par défaut.

Index : idx_email (unique), idx_country, idx_is_pedagogique.

Table products

Catalogue e-commerce. Les extensions pédagogiques :

  • price : DECIMAL(18,2) — précision étendue pour les valorisations d'entreprises (milliards). Les produits du catalogue standard restent en précision normale.
  • is_pedagogique : produits invisibles dans l'IHM admin — les 4 produits créés par V10 que les étudiants utilisent pour résoudre des énigmes.
  • pedagogique_protected : drapeau qui empêche OrderService de décrémenter le stock à la création d'une commande. Utilisé sur les produits dont les énigmes dépendent du stock exact (ex : Câble USB-C basique 1m a stock=60, l'énigme BAC1-2 calcule 8 × 60 = 480).

Index : idx_category, idx_name, idx_prod_pedagogique.

Table cart_items

Panier. Supporte deux cas de figure :

  • Panier authentifiéuser_id renseigné, session_id nul
  • Panier anonymesession_id renseigné, user_id nul

La FK sur user_id autorise NULL pour supporter les paniers anonymes. Au login, CartService migre les items du panier anonyme vers le compte utilisateur.

Table orders

Commande. L'status est un vrai ENUM MySQL avec six valeurs : PENDING, CONFIRMED, PROCESSING, SHIPPED, DELIVERED, CANCELLED.

Champs dédiés à la traçabilité paiement sans jamais stocker de données sensibles :

  • card_last4 : 4 derniers chiffres de la carte (affichage utilisateur)
  • cvv_verified : booléen — le CVV a-t-il été vérifié ? Le CVV réel n'est jamais stocké

Le champ is_test_data marque les commandes générées par les agents pédagogiques. Par défaut, elles sont exclues des stats affichées dans l'IHM admin, pour éviter de polluer les rapports avec des commandes d'entraînement.

Table order_items

Ligne de commande. quantity, unit_price et subtotal capturent le montant au moment de la commande (les prix ultérieurs en catalogue ne l'affectent pas).

Table admin_users

Comptes formateur. Cinq drapeaux de droits (can_access_chaos, can_access_monitoring, can_access_admin, can_access_jmeter, can_access_scripts) et un drapeau is_superadmin. Le superadmin bénéficie de protections codées en dur dans AdminUserService — voir Authentification admin.

Pas d'INSERT dans V1 : le bootstrap du superadmin est géré par AdminUserService @EventListener(ApplicationReadyEvent) à partir des variables d'environnement PERFSHOP_ADMIN_EMAIL et PERFSHOP_ADMIN_PASSWORD. Cette séparation évite d'avoir un hash BCrypt en dur dans le SQL de migration.

Table perfshop_license

Stocke la licence active. Contrainte id = 'current' : une seule ligne à la fois. Les upserts de LicenseService.activateLicense() font un UPDATE d'abord puis un INSERT si la ligne n'existe pas. Une contrainte CHECK valide le plan. Voir Système de licence.

Table pedagogique_sessions

Sessions individuelles du parcours pédagogique. Identifiée par un token UUID — pas de FK vers users car les sessions ne nécessitent pas de compte utilisateur. Architecture write-through : la base est toujours source de vérité, et un cache mémoire optionnel peut être activé à chaud par le formateur (voir Concept et architecture du parcours pédagogique).

Deux colonnes logique (logique_question_indices, logique_expected_hash) stockent le tirage des questions logique fait une seule fois au /join — cela élimine toute désynchronisation front/back.

Les timestamps joined_at et completed_at sont en millisecondes epoch (BIGINT), cohérent avec Instant.toEpochMilli() côté Java.

attempts_json est un TEXT (sans DEFAULT — MySQL 8 strict mode l'interdit sur TEXT) qui stocke un JSON plat { "bac1-0": 2, "bac1-1": 1, … } comptant les tentatives par étape.

V2 — Données utilisateurs

Crée des comptes de démonstration : quelques utilisateurs de test avec des profils variés (civilité, date de naissance, adresses dans plusieurs pays) pour alimenter les scénarios de test fonctionnel et les chaos métier.

V3 à V9 — Seeds catalogue

Sept migrations, une par catégorie majeure du catalogue :

Migration Catégorie Volumétrie approximative
V3 Informatique ~200 produits (ordinateurs, périphériques, composants, stockage, réseau…)
V4 Avions ~150 produits
V5 Voitures ~150 produits
V6 Entreprises ~230 produits (avec valorisations en DECIMAL(18,2))
V7 Hélicoptères ~170 produits
V8 Bateaux ~50 produits
V9 Motos ~50 produits

Le catalogue total oscille autour de 1 000 produits. Cette volumétrie est calibrée pour :

  • Tenir aisément en mémoire JVM avec une heap par défaut
  • Rendre visible un Chaos Performance (pagination lente, index manquant)
  • Offrir assez de diversité pour les critères de recherche des énigmes pédagogiques

Les images produits sont servies en local depuis le conteneur backend (/images/...) pour éviter toute dépendance externe.

V10 — Infrastructure pédagogique

Cette migration est critique et contient des éléments intangibles liés aux énigmes. Elle fusionne quatre dimensions :

1. Quatre produits pédagogiques

Ces produits ont is_pedagogique = TRUE et pedagogique_protected = TRUE. Ils sont invisibles dans l'IHM admin et leurs stocks ne sont jamais décrémentés. Chaque produit est soigneusement construit pour servir une énigme précise :

Produit Prix Stock Énigme
Câble USB-C basique 1m 22,00 € 60 BAC1-1 (filtre prix unique), BAC1-2 (stock × 8 bits)
Adaptateur CODE Pro USB-C 64,00 € 9999 BAC2-3 (recherche CODE), BAC2-4 (2⁶ = 64)
Lorem NVMe SSD 1To PCIe 4.0 255,00 € 32 BAC4-2 (header hex ASCII), BAC4-3 (0xFF = ff)
Câble Rainbow RGB braided 2m 34,99 € 9999 BAC5-2 (ROT13 « Follow the rainbow »)

Les prix et stocks sont référencés exactement par les énigmes. Toute modification casse les parcours — la migration V10 contient un avertissement explicite à ce sujet.

2. Cinq comptes agents BAC1 à BAC5

Les emails sont agent.bac1@perfshop.io à agent.bac5@perfshop.io. Les hash BCrypt sont intangibles — ils correspondent à des mots de passe que chaque parcours révèle à l'étudiant :

Agent Mot de passe Indice
agent.bac1 Cable22Go Donné en clair à l'étape 7 de BAC1
agent.bac2 Code64Exp6 Construit par déduction aux étapes 4 et 5 de BAC2
agent.bac3 Docker8080Get Encodé Base64 dans l'énoncé de l'étape 15 de BAC3
agent.bac4 Lorem255FF42 Encodé Base64 dans l'énoncé de l'étape 19 de BAC4
agent.bac5 Rsa3Xor51Pi14 Construit par calcul (XOR + Pi) dans BAC5

Les étudiants réutilisent ces credentials pour s'authentifier sur le shop dans la dernière étape de leur parcours et déclencher la page de succès.

3. Profils agents complets

Chaque agent reçoit un profil complet spy-themed (emprunté à V37 du schéma historique) : nom de code inspiré de personnages d'espionnage ou de cryptographie, adresse française, numéro de téléphone construit à partir de nombres symboliques, code postal thématique.

Agent Nom Ville Code postal
BAC1 Jason Bourne Paris 75007
BAC2 Alan Enigma Lyon 69001
BAC3 Lisbeth Docker Bordeaux 33000
BAC4 Ethan Lorem Toulouse 31000
BAC5 Ada Cipher Nantes 44000

4. Flags pedagogique_protected sur produits standards

Trois produits du catalogue standard (Basic USB Keyboard, Basic Optical Mouse, DDR4 16GB Kit (2x8GB)) reçoivent le drapeau pedagogique_protected = TRUE. Leur stock n'est plus décrémenté par OrderService. Ces produits sont utilisés dans les énigmes BAC3 (claviers et souris) et BAC5 (kit DDR4).

Pour le kit DDR4, le stock est forcé à 9999 car les énigmes BAC5-22/23 ne calculent pas sur son stock. Pour les deux autres, le stock réel est préservé car les énigmes en dépendent.

Index et performance

Les index placés dans V1__schema.sql reflètent les requêtes attendues par les services Spring Boot :

Table Index Requête ciblée
users idx_email (unique) Login, lookup par email
users idx_country Filtrages chaos métier par pays
users idx_is_pedagogique Exclusion des agents de l'IHM admin
products idx_category Filtrage par catégorie dans le catalogue
products idx_name Recherche texte approximative
products idx_prod_pedagogique Exclusion des produits pédagogiques
cart_items idx_user_id Récupération du panier utilisateur
cart_items idx_session_id Récupération du panier anonyme
orders idx_order_number (unique) Lookup par numéro de commande
orders idx_user_id Historique des commandes
orders idx_created_at Tri chronologique et stats temporelles
orders idx_is_test_data Exclusion rapide des commandes de test
admin_users idx_admin_email (unique) Login admin
pedagogique_sessions idx_ped_sessions_level Stats par niveau
pedagogique_sessions idx_ped_sessions_joined_at Tri par ancienneté, purge
pedagogique_sessions idx_ped_sessions_completed Filtrage sessions complétées

Encodage et moteur

Toutes les tables utilisent :

  • Moteur : InnoDB (transactions, contraintes FK)
  • Charset : utf8mb4
  • Collation : utf8mb4_unicode_ci

Le choix utf8mb4 (et non utf8) est impératif pour supporter les emoji dans les commentaires produits et les noms d'utilisateurs. MySQL considère utf8 comme un encodage legacy sur 3 octets.

Bootstrap d'une instance vide

Au premier démarrage, Flyway exécute toutes les migrations V1 à V10 dans l'ordre. À l'issue de V10, la base contient :

  • Une table users avec les comptes de test de V2 plus les 5 agents pédagogiques
  • Une table products avec ~1 000 produits des catalogues V3-V9 plus les 4 produits pédagogiques de V10
  • Des tables vides : cart_items, orders, order_items, pedagogique_sessions, perfshop_license
  • Une table admin_users vide — le superadmin est créé juste après par Spring (ApplicationReadyEvent)

Aucune intervention manuelle n'est nécessaire : la plateforme est immédiatement utilisable après docker compose up.