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_a → type = sale, cena_b → type = original, cena_c → type = cost, cena_montaze → type = install.
tax_rate
| Pole |
Typ |
Popis |
id |
uuid PK |
|
region_id |
uuid FK |
|
name |
string |
|
rate |
float |
|
is_default |
boolean |
|
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 |
|
Tabulky
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 |
|
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?