Vissza a Journal-hoz
storefronthu

Multi-tenant Storefront: egy backend, hat domain, egy on-call

Hogyan szolgal ki egy backend hat darab storefrontot Host-header feloldassal, es a cache-leak, amit elsore tenyleg elkovettunk.

A Netorigo Storefront keretrendszer egyetlen NestJS backendet (netorigo-app-backend) szolgal ki sok darab Next.js 16 frontendet, mindegyiket sajat domainen. Jelenleg hat storefront fut ezen az architekturan: nortinia.com, mediaorigo.com, plusz negy partner-domain. Az alabbiakban leirom, hogyan mukodik a tenant feloldas, miert nem hasitottunk per-tenant forkokat, es a hibat, amit elsore tenyleg elkovettunk.

Tenant feloldas: a Host header dont

A backend nem tudja onmagatol, hogy melyik tenant nevében szolgal ki egy kerest. A PublicStorefrontController legelso lepese az storefronts.findByDomain(req.headers.host) hivas, ami a storefront Prisma tablabol kikeresi a tenantot a normalizalt hostnev alapjan (lstrip www., lowercase). Az igy kapott tenantId az ALS (AsyncLocalStorage) contextbe kerul, es onnan minden tovabbi Prisma query automatikusan WHERE tenantId = ? filterrel mozog.

A tenant hatar tehat nem fizikai (kulon DB), hanem logikai (egy ID a row-on). Ez veszelyesen hangzik, de a Prisma middleware reteg minden read es write elott ellenorzi az ALS tenantot. Aki kifelejti a tenantId-t egy whereo-bol, az unit tesztben elhasal, mielott PR-t nyitna.

Miert egy backend, miert nem per-tenant

A design phase-ben komolyan vettuk a per-tenant fork opciot is. Minden tenantnak sajat repo, sajat deploy, sajat DB. Elony: tokeletes elszigeteles. Hatrany: hat tenant eseten hatszor annyi on-call, hatszor annyi migration coordinaltatas, es ha egy bug-fixet at kell huzni mindenhova, az hat PR es hat code review. Negy tenantnal mar nem allt jol a matek.

Egy backend + sok frontend: egyszerre kell upgradelni, egy on-call, egy CI pipeline. A frontend per-tenant kulonbozik (theme, navigacios menu, lokalizacios szovegek, MCP tool keszlet), de a domain logika es az adatmodell kozos.

Per-tenant cache namespace

A Redis cache kulcsai mindig tenant:<tenantId>:<resource>:<id> formaban allnak ossze. A CacheKeyService osztaly konstruktora kotelezoen vesz egy tenantId-t, igy nincs olyan code path, ahol valaki kifelejtene. Ezt onmaga a TypeScript tipus garantalja.

A hiba, amit elsore tenyleg elkovettunk

2025 januarjaban egy uj popular_products cache reteg kerult be, ami a tenant-agnosztikus popular_products_global kulcsra mentett. Naivan: "hat globalis lista, mind tenant ugyanazt latja." Csak hat NEM ugyanazt — a popular_products query a tenantId szerint scope-olja a order_line tablat, tehat amit cache-be raktunk az volt akarmelyik tenant top termeke, aki eppen elso volt a getPopular() hivasban. Ha mediaorigo.com kapott elobb terhelest, a nortinia.com is mediaorigos top-listat latott 5 percig.

A bug fel napig elt prod-ban, mielott egy partner jelezte. Fix: popular_products:tenant:<tenantId> cache kulcs, plusz egy E2E teszt ket tenanttal, ami biztosit, hogy a ket cache snapshot diszjunkt. A teszt azota egyetlen regresszion sem fogott — pont ezert van ott.

A tenant hatar olcson hibazik. A katasztrofat akkor okozza, ha a hatar magat sem teszteljuk.

A hat storefront halozatos szempontja

Ma hat tenant fut a backend mogott. Egy NestJS pod (4 vCPU, 8 GB RAM) alapertelmezesben kiszolgalja mindet, mert a Storefront fele a forgalom edge cache-bol erkezik (a 94% PDP hit rata reszleteit egy masik cikkben taglaljuk). A backend p99 latency 38 ms, a database query p95 12 ms — fejlettebb autoscale config nincs ra, mert nem kell. Csak Black Friday hetvegere bovitunk +2 podra, plusz a Postgres replica readonly route-jat aktivaljuk.

A tenant-onboard checklist

Uj tenant felvitele 4 lepes: (1) storefront row a DB-ben (domain + tenantId + theme JSON), (2) DNS CNAME a Vercel deploy URL-re, (3) HTTPS certificate (Vercel automatikus), (4) admin tenant-user inicializalas a /admin/tenants/[id]/init flow-n. Vegrehajthato tipikusan 35 perc alatt, code valtoztatas nelkul. Korabban a tenant bovites kodvaltozasos volt (config file commit), de azt 2025 marciusban megszuntettuk — mert egy tenant felvitele nem kellene PR-t igenyeljen.