Distribuovaný sync engine mezi Shoptetem (český zákaznický e-shop) a BrickLinkem (mezinárodní B2B marketplace pro LEGO). Bez něj by každý díl mohl kolidovat mezi dvěma prodejními kanály — s ním sklad drží, i když majitel spí. Kolem sync jádra postupně vyrostla i admin platforma, která z původního skriptu udělala nástroj pro každodenní provoz e-shopu.
Přehled
Jak to začalo
Minifigs.cz prodává unikátní LEGO dílky a minifigurky. Zákazníky obsluhuje Shoptet — přehledný český e-shop. Sklad doplňuje nákupy z BrickLinku — nejlepší mezinárodní zdroj LEGO dílů. Dva systémy, jeden sklad. A mezi nimi majitel s tabulkou, kterou musí ručně srovnávat.
Každý překlep znamenal overselling. Každé zapomenuté zapsání znamenalo díl chybějící v už odeslané objednávce. Nebyl to problém jednorázový — vznikal každý den znovu, s každou objednávkou z obou kanálů. Cíl projektu byl na papíře jednoduchý: obě platformy musí souhlasit bez lidské asistence.
Realita byla komplikovanější. Shoptet a BrickLink mají různé identifikátory dílů, různé názvy barev, různé pravidla pro storefront. Sync bez mapping systému by lil objednávky do zdi. Nejdřív se musel postavit slovník — pak teprve překlad.
Jak to funguje
Sync engine je Python + Flask + Gunicorn v Dockeru,
perzistence v SQLite. Shoptet informuje o změnách přes
webhooky (instantně, order:create/update/delete).
BrickLink webhooky pro inventář nepodporuje, tak se dohání
polováním každé 3 minuty. Kolem jádra vyrostla
admin platforma jako druhá vrstva — 17 šablon, 13 JS souborů,
žádný framework a žádný build krok, protože platforma má
jednoho maintainera.
- Picking workflow
- Mapping viewer (11 888)
- Audit log · Analytics
- Feedback · RBAC (magic link)
Bezpečnostní vrstva vede přes magic link (bez hesel, token přes
Resend API) a RBAC s rolemi admin (plný přístup)
a picker (jen picking obrazovky). Bootstrap adminů
z ADMIN_EMAILS env proměnné — never-lockout safeguard.
Jak to vyrostlo
Projekt nezačal jako „postavme admin platformu". Začal jako sync skript. Každá další vrstva přišla jako odpověď na konkrétní bolest — a všechny milníky mají svůj commit v gitu.
Výsledek: ze sync skriptu se stala platforma, kterou majitel používá v každodenním provozu. Bez tlaku na předem navržený monolit — každá vrstva přibyla, teprve když byla evidentně potřeba.
Tři největší výzvy
Řešení, na kterých mi projekt nejvíc zabral hlavu a která by se dala použít v jiných distribuovaných systémech.
1. Orphan recovery po deploy / OOM kill
Problém: Když proces umře uprostřed zpracování objednávky (deploy restart, out-of-memory na Railway), zůstanou „in-progress" záznamy v JSON stavu. Bez řešení by jeden špatný deploy znamenal, že se ručně dohledává, co se v okamžiku pádu rozsynchronizovalo.
Řešení: Startup catchup identifikuje stale
in-progress orders z mrtvých procesů. Podle stáří je buď
automaticky obnoví (window 30 min podle
ORPHAN_AUTO_RESUME_WINDOW_MINUTES), nebo označí
jako vyžadující manuální zásah. Bez tohohle safeguard by systém
nezvládl běžné Railway platform events.
2. Composite items (lightsabery)
Problém: Většina LEGO dílků je 1:1 — jeden
Shoptet SKU = jeden BrickLink item. Lightsabery se ale skládají
z rukojeti + čepele různých barev. Tracking klíč pro tyhle
případy nemůže být jen order_id, jinak by se dvě
různé kombinace rukojeť + čepel považovaly za duplikát.
Řešení: Vlastní tabulka composite_items
pro Shoptet varianty složené z více BrickLink dílů. Tracking klíč
je složený: ean_partno_bladecolor. Proti souběžnému
zápisu do stejného item pomáhají per-item locks v BrickLink klientu.
3. Rate limits + resumable mapping generator
Problém: Generování prvních 11 000 mapování znamenalo projít celý BrickLink inventář a spárovat ho s katalogem na Shoptetu. BrickLink API má rate limity, dohledávání běží hodiny, a když se proces zabije uprostřed, začínalo by se od nuly.
Řešení: MappingGenerator pracuje
checkpoint-based — po každé batch dávce ukládá stav do
mapping_generation_checkpoint.json. Kill process,
spusť znovu, pokračuje tam, kde skončil. Zároveň se ukládají
mezivýsledky do CSV pro backup.
Reálné metriky
Snapshot z produkčního stavu k dnešnímu dni — z databáze a repozitáře, žádné odhady.
[Doplnit business metriky od majitele — objem objednávek za den, čas ušetřený na manuální synchronizaci, případně počet předešlých oversellingů. Bez těchto dat je stránka technicky popsaná, ale postrádá impact story.]