Praktický návod

Jak provozovat více e-shopů z jedné administrace

Sdílej společná data a workflow, ale každou operaci prováděj v explicitním scope konkrétního obchodu.

30 minut · Multistore

Nejdřív stručně

Jedna aplikace neznamená jednu konfiguraci

Multistore může sdílet katalog, zákazníky nebo objednávkové workflow, zatímco doména, ceny, obsah, sklad, platby a oprávnění se liší podle obchodu.

Tenant nebo shop ID proto není volitelný filtr přidaný na konci dotazu. Je součástí identity, unikátních constraintů, cache, událostí, audit logu a každého background jobu.

Připrav si

Rozhodni, co se sdílí a co se odděluje

Pro každou entitu vytvoř matici vlastnictví. Nejasné „většinou společné“ vede k nečitelným override pravidlům.

  • Seznam obchodů, tenantů, domén, právních entit a administrátorů s jejich oprávněními.
  • Matici globální, tenant-specific a overridable pro produkty, ceny, obsah, zákazníky, sklady a objednávky.
  • Požadavky na izolaci dat, audit a případné regulatorní oddělení tenantů.
  • Strategii provozu: společná databáze se scope, samostatná schémata nebo databáze a důvod zvolené hranice.

Kroky 1 až 3

Udělej shop context povinný

Aktivní obchod se určí jednou na vstupu a dál se předává jako explicitní hodnota, ne jako skrytá globální proměnná.

1. Modeluj sdílení a override bez magie

  1. Globální produkt může držet SKU a technická data; shop listing viditelnost, lokální název, kategorii a ceník.
  2. Override ulož explicitně s jasným fallbackem na globální hodnotu. Null nesmí současně znamenat zdědit, skrýt i smazat.
  3. Objednávka, platba a doklad vždy patří jednomu shopu a nesmí měnit tenant. Sdíleného zákazníka váž na shop-specific souhlasy a historii.
  4. Unikátní constrainty navrhuj se shop_id tam, kde je hodnota unikátní jen v rámci obchodu, například slug nebo číslo lokální řady.
UNIQUE (shop_id, slug)
Oficiální PostgreSQL dokumentace ke constraintům

2. Izoluj dotazy, oprávnění a asynchronní práci

  1. Repository vyžaduje ShopId nebo tenant context a každý administrátorský požadavek ověří oprávnění pro konkrétní shop.
  2. Cache klíče, soubory, search indexy, exporty a message payloady obsahují shop_id. Chybějící scope odmítni, neodvozuj z poslední relace.
  3. Worker načte shop context ze zprávy a znovu ověří, že cílová data patří stejnému obchodu. Webový session context do fronty nepřenášej.
  4. PostgreSQL Row-Level Security může být další obranná vrstva, ale nenahrazuje správný aplikační návrh ani testy.
handler(message.shopId, message.entityId)
Oficiální PostgreSQL dokumentace k Row Security

3. Mapuj doménu na známý obchod

  1. Host mapuj přes allowlist aktivních domén na shop ID. Neznámou doménu odmítni a nevytvářej z ní tenant dynamicky.
  2. Canonical URL, odesílatel e-mailu, platební klíče a branding vybírej z ověřeného shop contextu, ne přímo z nedůvěryhodné Host hlavičky.
  3. Administrace umožní vědomě přepnout aktivní shop a stále ho viditelně zobrazuje. Hromadná akce explicitně ukáže všechny dotčené obchody.
  4. Konfiguraci verzuj a audituj: kdo změnil doménu, ceník, sklad nebo platební účet a kdy se změna projevila.
verified host → domain registry → shop_id
Oficiální Symfony Security dokumentace

Krok 4

Testuj především únik mezi obchody

Happy path jednoho shopu je jednoduchý. Kritická je záporná kontrola, že jiný shop data neuvidí ani nezmění.

  1. Použij stejné ID v různých shopech

    Ověř repository, URL, cache, export i worker. Požadavek shopu A nikdy nesmí vrátit objekt shopu B.

    php bin/phpunit --filter TenantIsolation
  2. Změň globální a lokální hodnotu

    Globální změna se projeví jen tam, kde není explicitní override; jeho odstranění obnoví zděděnou hodnotu.

  3. Pošli zprávu se špatným shop ID

    Worker ji bezpečně odmítne a zaloguje konflikt bez změny cílového objektu.

Když to zlobí

Nejčastější chyby

Produkt z jiného shopu se objevil v administraci

Některý dotaz, cache klíč nebo search index nemá tenant scope. Udělej shop context povinným argumentem a přidej regresní izolační test.

Override nelze vrátit na globální hodnotu

Model nerozlišuje zdědit, explicitní null a vlastní hodnotu. Zaveď jasný stav override a samostatnou operaci reset.

Worker používá konfiguraci posledního webového requestu

Tenant context je globální nebo session-based. Zpráva musí nést shop ID a worker z něj načte nový izolovaný context.

Neznámý Host zobrazí výchozí obchod

Fallback je bezpečnostní i SEO riziko. Doménu porovnej s allowlistem a neznámý host odmítni.

Hotovo

Jedna administrace sdílí práci, ne data omylem.

Shop context je teď povinný v dotazech, oprávněních, cache i zprávách. Sdílení a lokální override mají jasná pravidla a každý zásah zůstává auditovatelný.

Zavolejte mi

Zavolám vám následující pracovní den mezi 9:00 a 17:00.

Můžete mi také zavolat rovnou.

+420 605 181 728

Nechte mi telefonní číslo a pošlete žádost o zpětné zavolání.

Odesláním souhlasíte se zpracováním údajů pro vyřízení žádosti.