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 nonENUM— Hibernate valide le schéma contreVARCHAR. La contrainte métier (M,Mme,Mx) est gérée côté application parValidationService.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 champaddress). Cette décomposition est exigée par plusieurs chaos métier. postal_codelimité à 5 caractères : limite intentionnelle. Le Chaos Pédagogique Niveau 2+ tente volontairement d'insérer une valeur plus longue, ce qui déclenche uneDataTruncationMySQL observable dans les logs et les métriques — c'est une démonstration pédagogique de « bug silencieux sous chaos ».country: ISO2, défautFR.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êcheOrderServicede 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 1mastock=60, l'énigme BAC1-2 calcule8 × 60 = 480).
Index : idx_category, idx_name, idx_prod_pedagogique.
Table cart_items¶
Panier. Supporte deux cas de figure :
- Panier authentifié —
user_idrenseigné,session_idnul - Panier anonyme —
session_idrenseigné,user_idnul
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
usersavec les comptes de test de V2 plus les 5 agents pédagogiques - Une table
productsavec ~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_usersvide — 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.