Import a synchronizace dat¶
Přehled¶
Projekt pracuje se třemi zdroji dat, každý má jiný charakter:
| Zdroj | Typ | Frekvence | Kde |
|---|---|---|---|
| Starý e-shop (MariaDB) | Jednorázová migrace | 1x (test + ostrý přechod) | ESHOP/import/ |
| Pohoda (XML) | Full sync + inkrementální změny | 1× denně (full) + každých 20 min (diff) | products/Workers/ |
| Dodavatelé XML (11×) | Ceny, dostupnost | 1× denně, někteří max 2× | products/Workers/ |
Kde žije importní kód¶
ESHOP/import/ — jednorázová migrace¶
Samostatný projekt pro přechod ze starého e-shopu. Po úspěšném spuštění se archivuje. Migrační kód nepatří do produkční domény, nepatří do workers, nepatří do core modelu.
ESHOP/import/
src/
MariaDB/
ProductMigrator.php
CategoryMigrator.php
BrandMigrator.php
run.php
products/Workers/ — živé workery¶
Workery patří do tohoto projektu, protože sdílejí databázi, konfiguraci a triggerují search reindex.
products/Workers/
MeilisearchSetup.php # hotovo
SearchIndexWorker.php # hotovo
PohodaFullImportWorker.php # plán
PohodaDiffImportWorker.php # plán
SupplierImportWorker.php # plán (sdílený runner, konfigurace per dodavatel)
Architektura průtoku¶
zdroj (MariaDB / Pohoda XML / dodavatel XML)
→ system.import_raw # surová data, beze změny — střed systému
→ normalizer # mapování na nový model
→ catalog.* / pricing.* / inventory.*
→ system.migration_error # chyby s raw_data pro debug
→ system.migration_batch # stav a statistika dávky
Zásada: raw data se nikdy nepřepisují.
Zachrání to při: - změně mapování nebo opravě parseru - rollbacku importu - debugování — porovnání starý vs. nový systém - edge cases, které se objeví až za provozu
Architektonická rozhodnutí¶
system.import_raw je střed systému¶
Buffer pro všechna příchozí data. Nic nejde přímo do finálních tabulek.
CREATE TABLE system.import_raw (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
source varchar NOT NULL,
entity_type varchar NOT NULL,
external_id varchar,
import_batch_id uuid REFERENCES system.migration_batch(id),
payload jsonb NOT NULL,
payload_hash varchar,
imported_at timestamptz NOT NULL DEFAULT now(),
processed_at timestamptz,
status varchar NOT NULL DEFAULT 'pending',
error_message text
);
Správný průtok:
XML → RAW → NORMALIZER → UPSERT
Špatný průtok (nedělat):
XML → rovnou catalog.product
Idempotence je kritická¶
Pohoda diff i dodavatelé musí zvládnout stejný feed pustit 10× bez bordelu. Zajišťuje to:
payload_hash— skip pokud se nic nezměniloexternal_id— párování na existující záznamy- unique constraints v DB
- deterministic upsert (ne insert)
Identita produktu je největší budoucí problém¶
Mapování produktu přes zdroje nebude triviální. Typické problémy:
- jiný nebo chybějící EAN
- duplicitní EAN u různých dodavatelů
- varianty bez variant (dodavatel nezná varianty)
- změna SKU v čase
- různý naming stejného produktu
- dodavatel tvrdí jiný brand
Model mapování identity se vyladí až po prvních reálných XML a edge cases. Teď neřešit — cíl je průtok dat, ne dokonalá architektura.
Tracking v databázi¶
Máme hotové (migrace 011):
system.migration_batch— dávka importu (source, status, started_at, processed_rows, total_rows)system.migration_error— chyby s legacy_id, entity_type, raw_data
Chybí — první co udělat:
system.import_raw— viz struktura výše
Datová pole u importovaných entit¶
Každý importovaný záznam nese:
source— odkud pochází (legacy_mariadb,pohoda,supplier_xyz)external_id— původní ID ze zdrojového systémuimport_batch_id— reference namigration_batchpayload_hash— hash pro skip při opakovaném importu beze změn
Plán — co je třeba udělat¶
Fáze 0: Infrastruktura importu (udělat jako první)¶
- [ ] Vytvořit migraci
014_import_raw.sql— tabulkasystem.import_raw - [ ] Ověřit že
migration_batchamigration_errorjsou v DB
Fáze 1: Migrace ze starého e-shopu¶
- [ ] Zmapovat tabulky starého MariaDB (produkty, kategorie, brandy, ceny, sklad)
- [ ] Navrhnout mapování starý model → nový PostgreSQL model
- [ ] Napsat
ProductMigrator— plníimport_raw, pak normalizuje docatalog.* - [ ] Napsat
CategoryMigrator - [ ] Napsat
BrandMigrator - [ ] Spustit testovací import, ověřit přes public API
- [ ] Ostrý přechod
Fáze 2: Pohoda (hlavní sklad — full + inkrementální)¶
Pohoda exportuje dva typy XML:
- Full — kompletní snapshot, 1× denně (noc / ráno)
-
Diff — jen změněné záznamy, každých 20 minut přes URL — musí být idempotentní
-
[ ] Zjistit formát Pohoda XML (full vs. diff — stejné elementy?)
- [ ] Napsat
PohodaFullImportWorker - [ ] Napsat
PohodaDiffImportWorker - [ ] Párování přes EAN / SKU →
catalog.product_variant - [ ] Zápis cen přes
pricing.price_upsert() - [ ] Zápis skladu do
inventory.* - [ ] Nastavit docker crony
Fáze 3: Dodavatelé (11× — URL → XML)¶
- Každý dodavatel má vlastní URL na XML export
- Frekvence: 1× denně, někteří max 2×
-
Jeden sdílený runner, konfigurace per dodavatel
-
[ ] Definovat konfigurační strukturu (URL, frekvence, parser per dodavatel)
- [ ] Napsat
SupplierImportWorker— sdílený runner - [ ] Specifické XML parsery per dodavatel
- [ ] Párování přes EAN
- [ ] Nastavit crony
Přímý přístup do zdrojů¶
- Starý e-shop: přímý přístup do MariaDB (connection string v
.env) - Pohoda (full): URL na XML export — 1× denně
- Pohoda (diff): URL na XML diff — každých 20 min
- Dodavatelé: 11× URL na XML, každý vlastní endpoint a frekvence