Eshop — návrh datového modelu

Inspirační zdroje

Oblast Inspirace
Katalog Vendure
Pricing Medusa
Inventory Medusa
Attribute/Option rozlišení Sylius
API vlastní REST

Vrstva 1 — produkt, identita, stavy, dodavatel

Filosofie

Stávající eshop_produkty je agregát, který v sobě nese identitu, publikaci, lokalizace, ceny, sklad, importní stav a merchandising. Nový model tento agregát rozkládá do samostatných vrstev s jasně definovanými hranicemi.

Tabulky

product

Jádro produktu. Obsahuje pouze to, co je neměnné nebo provozně kritické napříč celým systémem.

Pole Typ Popis
id uuid PK Interní identifikátor
code string UK Obchodní kód — hlavní klíč pro import a párování
ean string EAN kód
brand_id uuid FK Vazba na výrobce
web_id int Mapovací klíč na starý systém při migraci
is_active boolean Zobrazovat na webu
is_discontinued boolean Vyřazený z nabídky
is_blocked boolean Blokovaný editorem
is_deleted boolean Označen ke smazání importem
is_new boolean Příznak nového produktu
is_clearance boolean Doprodej
hide_until timestamp Dočasně skrýt do tohoto data
created_at timestamp
updated_at timestamp

Stavové příznaky nejsou duplicity — každý má jiný provozní význam a jiný původ (import, editor, systém).

brand

Výrobce jako samostatná entita. Používá se ve výpisech, filtrech, akcích a importech.

Pole Typ Popis
id uuid PK
name string Název výrobce
slug string UK URL alias
website string Web výrobce
logo string Cesta k logu
is_local boolean Lokální výrobce
web_id int Mapovací klíč na starý systém

product_slug

Lokalizované URL aliasy produktu. Jeden řádek per jazyk.

Pole Typ Popis
id uuid PK
product_id uuid FK
locale string cs, sk, de, hu, pl
slug string UK URL alias pro daný jazyk

supplier

Dodavatel / zdroj importu.

Pole Typ Popis
id uuid PK
name string Název dodavatele
code string UK Kód dodavatele

product_supplier_feed

Oddělení canonical produktu od dodavatelského záznamu. Klíčový koncept — stávající systém míchá supplier state přímo do produktu.

Pole Typ Popis
id uuid PK
product_id uuid FK Vazba na canonical produkt
supplier_id uuid FK Vazba na dodavatele
supplier_code string Kód produktu u dodavatele
supplier_ean string EAN u dodavatele
lock_content boolean Nepřepisovat textová data při importu (mapuje neprepsat)
last_imported_at timestamp Datum posledního importu

Import logika: dodavatel insertuje pokud neexistuje code/ean → produkt vznikne jako neaktivní. Po prvním insertu dodavatel přepisuje pouze ceny a skladovou dostupnost. lock_content = true chrání textová data před přepsáním.


Vrstva 2 — lokalizace

Filosofie

Stávající model má lokalizace jako sloupce v produktu (nazev, nazev_sk, popis_sk atd.). Nový model má lokalizace jako samostatnou vrstvu. Fallback na výchozí jazyk (cs) řeší aplikační vrstva, ne databáze.

Tabulky

product_translation

Lokalizovaný obsah produktu. Jeden řádek per jazyk.

Pole Typ Popis
id uuid PK
product_id uuid FK
locale string cs, sk, de, hu, pl
name string Název produktu
name_heureka string Název pro Heureka feed (jiný než web název)
description_short text Krátký popis
description_long text Dlouhý popis
description_tech text Technické informace
out_of_stock_text text Text "není skladem" pro zákazníka
is_machine_translated boolean Strojový překlad — příznak pro editorial workflow
created_at timestamp
updated_at timestamp

brand_translation

Lokalizovaný název výrobce.

Pole Typ Popis
id uuid PK
brand_id uuid FK
locale string
name string

Vrstva 3 — varianty a atributy

Filosofie — Sylius rozlišení Option vs. Attribute

Typ Definice Vliv na SKU
Option Osa varianty (velikost, barva) Ano — každá kombinace = jiná varianta
Attribute Popisná vlastnost (materiál, váha) Ne — pouze informační

Stávající systém má dvě historické osy: varianty a barvy. Ty se mapují jako dva option_group záznamy s type = variant a type = color.

Tabulky

product_variant

Konkrétní SKU — prodejná jednotka produktu.

Pole Typ Popis
id uuid PK
product_id uuid FK
code string UK Vlastní kód varianty
ean string EAN varianty
is_active boolean
is_default boolean Výchozí varianta produktu
sort_order int
created_at timestamp
updated_at timestamp

product_variant_translation

Pole Typ Popis
id uuid PK
variant_id uuid FK
locale string
name string Lokalizovaný název varianty

option_group

Osa varianty (např. Velikost, Barva). Vazba na produkt.

Pole Typ Popis
id uuid PK
product_id uuid FK
code string Kód osy (size, color, …)
type string variant nebo color — mapuje historické typy
sort_order int

option_group_translation

Pole Typ Popis
id uuid PK
option_group_id uuid FK
locale string
name string Lokalizovaný název osy

option_value

Konkrétní hodnota osy (např. XL, Červená).

Pole Typ Popis
id uuid PK
option_group_id uuid FK
code string
color_hex string Hex kód barvy (pro type = color)
image string Obrázek vzorníku
sort_order int

option_value_translation

Pole Typ Popis
id uuid PK
option_value_id uuid FK
locale string
name string

product_variant_option_value

Pivot — varianta je průnikem hodnot os.

Pole Typ
variant_id uuid FK
option_value_id uuid FK

attribute

Popisná vlastnost produktu — neovlivňuje SKU.

Pole Typ Popis
id uuid PK
code string UK
type string text, number, boolean

attribute_translation

Pole Typ Popis
id uuid PK
attribute_id uuid FK
locale string
name string

product_attribute_value

Hodnota atributu pro konkrétní produkt a locale.

Pole Typ Popis
id uuid PK
product_id uuid FK
attribute_id uuid FK
value string
locale string Atributy mohou být lokalizované

Vrstva 4 — ceny (Medusa pricing model)

Filosofie

Varianta nemá jednu cenu. Varianta má price_set ze kterého se vybírá cena podle kontextu (region, měna, typ). Vstupní ceny jsou odděleny od odvozených. Master měna je CZK, přepočet na EUR probíhá v aplikační vrstvě přes exchange_rate na regionu.

Regiony

Kód Měna Poznámka
CZ CZK Master region
SK EUR Jiný režim DPH
DE EUR
HU EUR
PL EUR

Tabulky

region

Pole Typ Popis
id uuid PK
code string UK CZ, SK, DE, HU, PL
name string
currency_code string CZK, EUR
tax_mode string inclusive nebo exclusive
exchange_rate float Přepočet z CZK

price_set

Kontejner cen pro variantu.

Pole Typ Popis
id uuid PK
variant_id uuid FK 1:1 s variantou
created_at timestamp

price

Konkrétní cena v kontextu regionu, měny a typu.

Pole Typ Popis
id uuid PK
price_set_id uuid FK
currency_code string CZK, EUR
region_code string Null = platí pro všechny regiony
amount int V haléřích/centech — nikdy float
type string sale, original, cost, install
min_quantity int Množstevní cena od
max_quantity int Množstevní cena do
valid_from timestamp Platnost od
valid_to timestamp Platnost do

Mapování starých cen: cena_atype = sale, cena_btype = original, cena_ctype = cost, cena_montazetype = install.

tax_rate

Pole Typ Popis
id uuid PK
region_id uuid FK
name string
rate float
is_default boolean

promotion

Akce a výprodeje jako cenová pravidla.

Pole Typ Popis
id uuid PK
code string UK
name string
target_type string product, brand, category
target_id uuid ID cílové entity
discount_type string percent, fixed
discount_value float
priority int Řeší konflikt více akcí
only_in_stock boolean Akce jen pokud je produkt skladem
valid_from timestamp
valid_to timestamp
is_active boolean

Vrstva 5 — sklad a dostupnost (Medusa inventory model)

Filosofie

Fyzický stock je oddělen od obchodní dostupnosti. Zákazník vidí obchodní sdělení, systém pracuje s fyzickými zásobami.

Tabulky

inventory_item

Fyzická věc která se skladuje. Nese rozměry a váhu.

Pole Typ Popis
id uuid PK
variant_id uuid FK 1:1 s variantou
sku string UK
requires_shipping boolean
weight float kg
width float cm
height float cm
length float cm
created_at timestamp
updated_at timestamp

stock_location

Sklady a lokace.

Pole Typ Popis
id uuid PK
code string UK
name string
is_central boolean Mapuje pohled eshop_produkty_sklad_centrala

inventory_level

Fyzický stock per lokace.

Pole Typ Popis
id uuid PK
inventory_item_id uuid FK
location_id uuid FK
stocked_quantity int Fyzicky na skladě
reserved_quantity int Rezervováno v otevřených objednávkách
incoming_quantity int Na cestě (na_ceste)

Dostupné množství = stocked_quantity - reserved_quantity

supplier_availability

Dodavatelská dostupnost — oddělená od fyzického stocku.

Pole Typ Popis
id uuid PK
inventory_item_id uuid FK
supplier_id uuid FK
availability_code string in_stock, on_order, unavailable
availability_text string Textový popis od dodavatele
eta timestamp Očekávané datum naskladnění
updated_at timestamp

availability_text

Lokalizované obchodní sdělení pro zákazníka.

Pole Typ Popis
id uuid PK
inventory_item_id uuid FK
locale string
text string Text zobrazený zákazníkovi
display_type string in_stock, low_stock, out_of_stock, on_order

Vrstva 6 — kategorie a kolekce

Filosofie

Stávající model má kategorie ve dvou reprezentacích (pipe-separated sloupec + pivot tabulka) a kód je mezi nimi synchronizuje ručně. Nový model má jediný canonical zdroj pravdy. Struktura stromu je implementována jako closure table — pro 1700 kategorií s hlubokým stromem je to výrazně lepší než parent_id při filtrování produktů podle kategorie a všech podkategorií.

Closure table — princip

Tabulka category_closure obsahuje řádek pro každý pár předek → potomek včetně uzlu samotného (depth = 0).

-- Celý podstrom (pro filtrování produktů)
SELECT descendant_id FROM category_closure WHERE ancestor_id = :id

-- Breadcrumb
SELECT ancestor_id FROM category_closure
WHERE descendant_id = :id ORDER BY depth DESC

-- Přímé děti
SELECT descendant_id FROM category_closure
WHERE ancestor_id = :id AND depth = 1

Tabulky

category

Pole Typ Popis
id uuid PK
parent_id uuid FK Redundantní, ale záměrně — pro rychlý přístup k přímému rodiči
code string UK
legacy_id int Staré číselné ID pro migraci
depth int Hloubka v stromu
sort_order int
is_active boolean
is_internal boolean Interní kategorie — nezobrazuje se na webu
created_at timestamp
updated_at timestamp

category_closure

Pole Typ Popis
ancestor_id uuid FK
descendant_id uuid FK
depth int 0 = uzel sám na sebe

category_translation

Pole Typ Popis
id uuid PK
category_id uuid FK
locale string
name string
description text
slug string UK
meta_title string
meta_description string

product_category_assignment

Jediný canonical zdroj přiřazení produktů do kategorií.

Pole Typ Popis
id uuid PK
product_id uuid FK
category_id uuid FK
is_primary boolean Hlavní kategorie pro breadcrumb a URL
sort_order int

category_migration_map

Pomocná tabulka pro migraci — mapuje staré pipe-separated hodnoty na nové UUID.

Pole Typ Popis
id uuid PK
legacy_id int Staré číselné ID
new_category_id uuid FK
legacy_pipe_value string Hodnota jak vypadala v pipe listu (\|123\|)
is_mapped boolean Stav mapování

collection

Kurátorovaná nebo dynamická skupina produktů napříč kategoriemi (Vendure koncept). Mapuje staré slider1, slider2 a podobné merchandisingové skupiny.

Pole Typ Popis
id uuid PK
code string UK
type string manual, automated, seasonal
is_active boolean
sort_order int

collection_translation

Pole Typ Popis
id uuid PK
collection_id uuid FK
locale string
name string
slug string UK
description text

collection_product

Pole Typ Popis
collection_id uuid FK
product_id uuid FK
sort_order int

Vrstva 7 — média, vazby, merchandising, metadata

Tabulky

product_media

Média produktu nebo varianty.

Pole Typ Popis
id uuid PK
product_id uuid FK
variant_id uuid FK Nullable — médium může patřit variantě
type string image, video, document
url string
alt string Alt text
sort_order int
is_primary boolean Hlavní foto

product_relation

Vazby mezi produkty s explicitním typem.

Pole Typ Popis
id uuid PK
product_id uuid FK
related_product_id uuid FK
relation_type string related, category_related
sort_order int

product_accessory

Příslušenství — odděleno od relations, jiné chování v UI a košíku.

Pole Typ Popis
id uuid PK
product_id uuid FK
accessory_product_id uuid FK
sort_order int

product_merchandising

Interní merchandisingové příznaky.

Pole Typ Popis
id uuid PK
product_id uuid FK
is_featured boolean Doporučujeme
is_bestseller boolean Nejprodávanější
is_recommended boolean
is_sale boolean Akční nabídka
priority int Pořadí ve výpisech
badge_label string Štítek (stitek)
in_slider_1 boolean
in_slider_2 boolean
slider_1_priority int
slider_2_priority int

product_external_metadata

Feed-specific a remarketing metadata. EAV struktura pro flexibilitu bez změny schématu.

Pole Typ Popis
id uuid PK
product_id uuid FK
channel string google, heureka, zbozi
key string Název parametru
value string Hodnota

Mapuje: google_param, popis_ga, kategorie_ga, gensace_* a podobná pole.

product_compliance

PHE a recyklační poplatky.

Pole Typ Popis
id uuid PK
product_id uuid FK
fee_type string phe, recycling
fee_code string Kód skupiny (skupina_phe, phe_kod)
linked_product_id uuid FK Přidružená položka automaticky přidaná do košíku
fee_amount float Výše poplatku

product_search_document

Denormalizovaný dokument per locale pro search engine (Meilisearch / Typesense).

Pole Typ Popis
id uuid PK
product_id uuid FK
locale string
search_text text Fulltext — název, kód, EAN, výrobce
structured_data jsonb Cena, dostupnost, facety — vše bez joinů
indexed_at timestamp Datum poslední synchronizace

Poznámky k migraci

  • product.web_id a brand.web_id slouží jako mapovací klíče na starý systém
  • category_migration_map překládá staré pipe-separated hodnoty na nové UUID kategorie
  • option_group.type rozlišuje historické typy varianta a barva
  • product_supplier_feed.lock_content mapuje starý příznak neprepsat
  • Order item snapshot semantics musí zůstat zachovány — objednávka ukládá snapshot produktu, nespoléhá na aktuální stav

Otevřené otázky ke konzultaci

  • Struktura order item snapshotu (vrstva 8)
  • Facety pro filtrování — samostatná vrstva nad atributy nebo odvozeno z product_attribute_value?
  • Search document sync strategie — trigger nebo job?