Modèle de Données — Conceptuel (MERISE 2) & Physique
0,N · 1,1 · 1,N · 0,1 Cardinalités (min,max)
Identifiants : soulignés (id_template). FKs : en italique vert (creator_id).
Cardinalités : 0,1 optionnel max 1 — 1,1 obligatoire exactement 1 — 0,N optionnel illimité — 1,N obligatoire illimité.
Le modèle compte 23 entités organisées autour de TEMPLATE (cocktail).
Chaque diagramme thématique ci-dessous détaille les associations et cardinalités précises pour un cluster du modèle.
Cœur du modèle : un TEMPLATE est composé de plusieurs STEP, chacun référençant un INGREDIENT positionné dans un ROLE (R1-R9) avec une QUANTITY structurée.
CROSS_ROLE_INDEX ; 14 ingrédients concernés, ex : champagne = R1+R5, velvet falernum = R2+R3).
Hiérarchie unifiée GROUP auto-référente remplaçant les anciens champs grp et subgrp dénormalisés.
Hiérarchie unifiée PLACE auto-référente sur 5 niveaux : continent → country → region → city → bar.
PERSON unifiée (bartenders + célébrités + auteurs) avec rôles multiples, et WORK unifié (movies + books + songs).
Vocabulaires contrôlés (AROMA_PROFILE, ACCORD, FAMILY, ALLERGEN_VOCAB) + service (GLASS, TECHNIQUE, ICE).
607 occurrences. Voir aussi le découpage conceptuel en 5 sous-entités logiques.
id_template name, yearvolume, diff, timeabv, abv_servi sweetness, acidity, bitterness, intensitybodyiba, iconic, veganhistory, processgroup_id, subgroup_id, archetype_idcreator_id, celebrity_ids[], work_ids[]continent_id, country_id, region_id, city_id, bar_idaccord_ids[], aroma_profile_ids[], allergen_ids[]glass_id, technique_id, ice_id~12 000 occurrences. Identifiée par position relative dans TEMPLATE.steps[].
id_step template_id ingredient_id role_id cat qty quantity_parsed refs[] optional, txt~700 occurrences. Couvre ingrédients-matière (R1-R7) et étiquettes-métier (R8-R9). Voir sous-types XT.
name cats[] abv, veganprofile_aroma[]familles[]substituts[]origin9, 25, 24, 9, 22 et ~3 occurrences. Entités de référence avec PKs textuelles ou numériques.
56 occurrences (11 niveau 1 + 45 niveau 2). Unifie grp et subgrp.
id_group labelparent_id depth children[] 98 occurrences. Multi-rôle : un même PERSON peut être writer + bartender + celebrity.
id_person nameroles[] 8 occurrences. Type polymorphique : movie | book | song.
id_work titletype yearauthor_id 238 occurrences (12 continents, 58 countries, 102 regions, 21 cities, 45 bars). Le 12e membre continent, « International », est un catch-all non géographique pour les cocktails d'origine mondiale/sans pays unique — couverture continent_id totale (607/607).
id_place nametype level parent_id 12 + 134 + 23 + 6 occurrences. Promotion en entités à part entière des anciens strings volatiles.
id label code, icon 3 401 quantités parsées. Embedded value object (pas une vraie entité avec PK propre).
value unit modifier raw | Association | Entités | Cardinalités | Sémantique |
|---|---|---|---|
| composer | TEMPLATE — STEP | 1,N — 1,1 | Composition ordonnée d'un cocktail |
| référencer | STEP — INGREDIENT | 1,1 — 0,N | Choix d'ingrédient |
| positionner | STEP — ROLE | 1,1 — 0,N | Rôle fonctionnel R1-R9 |
| quantifier | STEP — QUANTITY | 1,1 — embedded | Volume / pièces (value object) |
| appartenir | INGREDIENT — ROLE | 1,N — 1,N | Multi-rôle (champagne = R1+R5) |
| classer | TEMPLATE — GROUP | 2,2 — 0,N | Toujours 2 groups (niveau 1 + 2) |
| hiérarchiser (réflexive) | GROUP — GROUP | 0,1 — 0,N | parent/enfant taxonomique |
| incarner | TEMPLATE — ARCHETYPE | 1,1 — 1,N | Famille structurelle |
| localiser | TEMPLATE — PLACE | 0,5 — 0,N | Chaîne géographique (jusqu'à 5 niveaux) |
| hiérarchiser (réflexive) | PLACE — PLACE | 0,1 — 0,N | parent/enfant géographique |
| être créé par | TEMPLATE — PERSON | 0,1 — 0,N | creator role=bartender |
| associer à | TEMPLATE — PERSON | 0,N — 0,N | celebrities role=celebrity |
| apparaître dans | TEMPLATE — WORK | 0,N — 0,N | Références culturelles |
| écrire | PERSON — WORK | 0,N — 0,1 | Auteur d'une œuvre |
| être connu | TEMPLATE — ALIAS | 0,N — 1,1 | Noms alternatifs |
| présenter | TEMPLATE — AROMA_PROFILE | 1,N — 0,N | Profils aromatiques |
| présenter | INGREDIENT — AROMA_PROFILE | 0,N — 0,N | Profil natif de l'ingrédient |
| accorder avec | TEMPLATE — ACCORD | 0,N — 0,N | Accords gastronomiques |
| contenir | TEMPLATE — ALLERGEN_VOCAB | 0,N — 0,N | Allergens propagés via steps |
| contenir | INGREDIENT — ALLERGEN_VOCAB | 0,N — 0,N | Allergens natifs |
| appartenir | INGREDIENT — FAMILY | 0,N — 0,N | Familles olfactives |
| servir dans | TEMPLATE — GLASS | 1,1 — 0,N | Verre de service |
| préparer par | TEMPLATE — TECHNIQUE | 1,1 — 0,N | Technique principale |
| refroidir | TEMPLATE — ICE | 0,1 — 0,N | Type de glace |
| dériver (réflexive) | TEMPLATE — TEMPLATE | 0,N — 0,N | Variantes & cousins |
| substituer (réflexive) | INGREDIENT — INGREDIENT | 0,N — 0,N | Substituts possibles |
TEMPLATE.abv et TEMPLATE.abv_servi sont fonctionnellement déterminés par STEP × INGREDIENT.abv × TECHNIQUE.dilution_taux. Recalculé au boot via _deriveTemplateAbv().
TEMPLATE.allergen_ids est l'union des INGREDIENT.allergen_ids de tous ses STEP, plus les allergens R9 déclarés.
group_id doit correspondre au string grp, idem subgroup_id ↔ subgrp, creator_id ↔ creator, etc. Chaque FK doit correspondre à la valeur de référence de l'entité cible.
has_dairy ⇔ allergen_ids contient l'id "dairy", idem pour eggs, gluten, etc.
parent_id doit pointer vers un PLACE de niveau strictement inférieur. Ex : un city (level=4) ne peut avoir comme parent qu'un country (level=2) ou region (level=3).
id_person. Garanti par la déduplication par nom à la construction.
L'entité TEMPLATE (~40 attributs) est conceptuellement découpée en 6 sous-entités logiques (toutes en 1:1 avec TEMPLATE, donc physiquement dans la même structure mais facilitant la lecture du modèle).
| Sous-entité | Attributs | Rôle |
|---|---|---|
| TEMPLATE_CORE | name, year, group_id, subgroup_id, archetype_id, iba, iconic, iba_inclusion_year, iba_removed_year | Identification + taxonomie |
| TEMPLATE_COMPOSITION | volume, abv, abv_servi, ingredients_count, spirits_count, vegan, allergen_ids[], volume_variable | Chimie & composition |
| TEMPLATE_SENSORY | sweetness, acidity, bitterness, body, intensity, aroma_profile_ids[] | Profil gustatif |
| TEMPLATE_SERVICE | glass_id, technique_id, ice_id, diff, time, is_shaken/stirred/built/layered | Préparation & service |
| TEMPLATE_NARRATIVE | history, process, creator_id, country_id, region_id, city_id, bar_id, continent_id, work_ids[], celebrity_ids[], alias_ids[] | Storytelling & culture |
| TEMPLATE_FILTERS | has_X[] booléens, accord_ids[] | Filtres et pré-calculs |
Tableau JavaScript de 607 objets, un par cocktail. Implémentation physique de l'entité TEMPLATE et de ses clés étrangères vers les entités normalisées.
| Champ | Type physique | Description |
|---|---|---|
id | Number | Clé primaire (entier unique) |
name | String | Nom du cocktail |
year | Number | null | Année de création |
grp / subgrp | String | Libellés taxonomiques (miroir lisible des FK) |
archetype | String | Famille structurelle (22 valeurs) |
iba, iconic | Boolean | Membre IBA actuel ou passé (modèle inclusif : le tag suit l'historique début→fin ; un retiré reste iba=true avec iba_removed_year), statut iconique |
iba_inclusion_year, iba_removed_year | Number | null | Période IBA (entrée / retrait). Dérivé everIBA = a figuré sur la liste à un moment ; le tag IBA suit cet historique (cocktails retirés affichés ex-IBA) |
abv, abv_servi | Number | Titrage alcoolique (calculé au boot) |
vegan | Boolean | Calculé depuis les ingrédients |
sweetness, acidity, bitterness, intensity | Number (0-5) | Profil gustatif |
body | String | Léger / moyen / corsé |
steps | Array<Object> | Étapes de composition (entité STEP) |
history, process | String | Récit + préparation |
has_citrus, has_dairy, has_eggs, … | Boolean | Pré-calculs de filtrage |
is_shaken, is_stirred, is_built, is_layered | Boolean | Technique principale |
| FK | Type | Cible | Couverture |
|---|---|---|---|
group_id | String | GROUPS (depth 1) | 607 / 607 |
subgroup_id | String | GROUPS (depth 2) | 607 / 607 |
creator_id | String | null | PERSONS (bartender) | 93 / 607 |
celebrity_ids[] | Array<String> | PERSONS (celebrity) | 4 / 607 |
work_ids[] | Array<String> | WORKS | 48 / 607 |
continent_id | String | null | PLACES (continent) | 607 / 607 |
country_id | String | null | PLACES (country) | 607 / 607 |
region_id | String | null | PLACES (region) | 241 / 607 |
city_id | String | null | PLACES (city) | 126 / 607 |
bar_id | String | null | PLACES (bar) | 110 / 607 |
accord_ids[] | Array<String> | ACCORDS | 607 / 607 |
aroma_profile_ids[] | Array<String> | AROMA_PROFILES | 607 / 607 |
allergen_ids[] | Array<String> | ALLERGENS_VOCAB | 282 / 607 |
| Champ | Type | Description |
|---|---|---|
ref | String | Nom de l'ingrédient (→ INGREDIENT) |
role | String | R1 à R9 |
cat | String | Sous-catégorie du rôle |
qty | String | Quantité brute (texte original préservé) |
quantity_parsed | Object | {value, unit, modifier, raw} — 3 394 steps parsés |
optional | Boolean | Ingrédient facultatif |
Objet structuré par rôle fonctionnel (R1 à R9). Chaque rôle contient des catégories, chaque catégorie une liste d'items. Implémentation physique de l'entité INGREDIENT.
| Rôle | Intitulé | Catégories | Items |
|---|---|---|---|
R1 | Base Alcoolisée | 23 | 100 |
R2 | Modificateur | 19 | 95 |
R3 | Sucrant / Sirop | 7 | 37 |
R4 | Acide & Jus | 15 | 41 |
R5 | Allongeur | 11 | 47 |
R6 | Liant | 15 | 34 |
R7 | Bitters & Épices | 6 | 37 |
R8 | Dressing / Présentation | 10 | 122 |
R9 | Fiche de service (verre, glace, technique, profil…) | 13 | 161 |
Huit structures Map construites au démarrage, chacune indexée par sa clé primaire textuelle. Ce sont les tables de référence vers lesquelles pointent les clés étrangères de DATA_TMP.
| Table (Map) | PK (pattern) | Entrées | Champs principaux |
|---|---|---|---|
GROUPS | grp-N / sub-N | 57 | label, parent_id, depth, children[] |
PERSONS | per-N | 71 | name, roles[] |
WORKS | wrk-N | 8 | title, type, year, author_id |
PLACES | plc-N | 238 | name, type, level, parent_id |
ACCORDS | acc-N | 12 | label |
AROMA_PROFILES | apr-N | 20 | label |
ALLERGENS_VOCAB | all-N | 6 | code, label, icon |
FAMILIES | fam-N | 23 | label |
Tables lues au démarrage pour peupler les champs dérivés sans aucun parsing ni regex : elles sont la source unique des champs autrefois extraits de la prose ou du nom.
| Seed | Entrées | Champ cible |
|---|---|---|
TEMPLATE_ORIGINS | 607 | origin {country,region} → géo (country_id / region_id / city_id) |
TEMPLATE_CREATORS | 93 | creator → creator_id (PERSON) |
TEMPLATE_ATTRIBUTION | 198 (≠ anonyme) | attribution_type (défaut anonyme) |
TEMPLATE_ARCHETYPE | 607 | archetype |
TEMPLATE_BAR | 110 | signature_bar → bar_id (PLACE) |
TEMPLATE_CULTURAL | 48 | apparitions → work_ids / celebrity_ids |
CITRUS_ITEMS / HERB_ITEMS / BUBBLE_ITEMS | 44 / 17 / 9 | has_citrus / has_herbs / has_bubbles |
TECH_FLAGS | 52 | is_shaken / is_stirred / is_built / is_layered |
57 entrées : 11 grands groupes (depth 1) et 46 sous-groupes (depth 2). Auto-référence via parent_id.
| Type | Niveau | Entrées |
|---|---|---|
| continent | 1 | 12 |
| country | 2 | 58 |
| region | 3 | 102 |
| city | 4 | 21 |
| bar | 5 | 45 |
Déduplication stricte par (type, name) : un lieu de même nom et type est unique, quel que soit son rattachement parent.
Structures construites une fois au démarrage pour accélérer filtres et recherches. Ce sont des vues : leur source de vérité est toujours DATA_TMP ou les tables d'entités.
| Index | Structure | Source | Usage |
|---|---|---|---|
ITEM_INDEX | Map nom → données | DATA_ING | Recherche par ingrédient |
SUB_CATS | Object grp → [subgrps] | GROUPS | Boutons de filtre taxonomique |
ORIGIN_TO_TPLS | Object pays → Set(ids) | PLACES + country_id | Worldmap (comptage par pays) |
WORLDMAP_CONTINENTS | Object continent → [pays] | PLACES (hiérarchie) | Worldmap (groupement) |
GROUP_BY_LABEL | Map label → id | GROUPS | Résolution label → FK (filtrage) |
PLACE_BY_NAME | Map nom → id | PLACES | Résolution nom → FK |
PERSON_BY_NAME | Map nom → id | PERSONS | Résolution nom → FK |
CROSS_ROLE_INDEX | Map ingrédient → rôles | DATA_ING | Pills multi-rôle |
r9Index | Map item → templates | DATA_TMP | Filtres de service (R9) |
ORIGIN_TO_TPLS, WORLDMAP_CONTINENTS) et taxonomiques (SUB_CATS) sont dérivées des entités normalisées (PLACES, GROUPS), qui constituent la source unique de vérité. L'interface compare via les clés étrangères (group_id, country_id) résolues par les index inversés.
Ordre de dérivation des structures, du chargement des données brutes jusqu'aux vues prêtes pour l'interface.
Cette séquence s'exécute après le chargement du DOM. Chaque étape ne dépend que des structures construites aux étapes précédentes, garantissant un ordre déterministe.
has_*/is_*, archétype, en-tête d'history) proviennent
désormais des tables structurées (seeds, §14) ou de compute sur le modèle. Les parseurs
_parseHistMeta/_histAttribution et les regex creatorPatterns /
BAR_REGEX / ARCH_BY_NAME / *_PATTERNS ont été retirés ; un audit
(noregex_guard) interdit leur réapparition.
audit2 : 14 modules, 101 tests (cohérence données / éditoriale /
fonctionnelle, invariants property-based des filtres, FK des entités, non-régression BDD-orientation),
sur un seul boot jsdom. Hygiène : seuls les contrôles porteurs de signal sont conservés ; les vérifications
garanties par construction (existence des FK produites par getOrCreate*) sont élaguées.
grp ↔ group_id, country_origin ↔ country_id). L'interface filtre via les FK ; les libellés restent disponibles pour l'affichage.
ITEM_INDEX préserve la casse d'affichage, ITEM_INDEX_LC sert aux comparaisons.
abv, abv_servi et vegan sont recalculés à partir de la composition réelle (ingrédients × titrages × dilution technique) plutôt que stockés en dur.