本文へスキップ
Z-CMS is being built in the open on GitHub. コミュニティに参加する

記事一覧

Z-CMS における Multi-tenancy の実装

2026年7月14日 · Z-SOFT Admin · 約 6 分で読めます

1 つの CMS Core で複数 website を安全に分離する architecture

Multi-tenancy はすべての layer に影響する

Multi-tenancy は tenant_id column を追加するだけではありません。Authentication、authorization、database、cache、storage、queue、search、plugin、theme、logging、billing のすべてに影響します。1 layer でも tenant context を失うと cross-tenant data leak が起こり得ます。

Tenant resolution

Z-CMS は custom domain、subdomain、path、X-Tenant-Code のような trusted header から tenant を resolve できます。Tenant を特定できない request は business service に入る前に拒否します。User が request body に指定した tenantId は信頼しません。

Request-scoped tenant context

Resolution 後、tenant context を request scope に設定し、service call 全体に伝搬させます。Application code は domain を再解析したり任意の tenantId を受け取るのではなく、tenantContext.getTenantId() を利用します。

Request
→ Authentication
→ Tenant Resolution
→ Authorization
→ Database / Cache / Storage
→ Queue / Search
→ Plugin / Theme
→ Logging

Database isolation strategy

小規模 tenant には shared database/shared schema、より高い isolation には separate schema、enterprise には dedicated database を選択できます。Application は共通 repository contract を利用し、hybrid model に対応します。

Tenant-aware repository と Row-Level Security

Repository は現在の tenant に bind され、developer が各 query に WHERE tenant_id を書き忘れる risk を減らします。PostgreSQL Row-Level Security を defense-in-depth として使えば、application filter が欠けても database policy が row access を制限できます。

tenant-a:post:123
tenants/tenant-a/media/image.jpg
tenant-a:plugin-seo:settings

Cache、storage、queue、search isolation

Cache key には必ず tenant prefix を含めます。Storage API は tenants/{tenantId}/... path を自動追加し、plugin に arbitrary path を許可しません。Background job は tenant context を保持し、search query は tenant filter または dedicated index を使用します。

Authentication、membership、permission

Login 済み user がすべての tenant に access できるわけではありません。まず tenant membership、次に role と content.publish などの permission を確認します。同じ user が tenant A では Owner、tenant B では Editor、tenant C では Viewer になることがあります。

Tenant ごとの plugin と theme

各 tenant は異なる plugin、version、settings、active theme を利用できます。Plugin settings は tenant + plugin ID で scope されます。Runtime は Core から immutable tenant context を受け取り、別 tenant を選択できません。

Logging、rate limit、maintenance

Structured log には tenantId、requestId、userId、pluginId を含めます。Rate limit は IP、user、tenant、API key、plugin ごとに適用できます。Reindex や migration は tenant 単位の job に分割し、1 tenant が platform 全体を block しないようにします。

Multi-tenancy の test

Test では常に 2 つ以上の tenant を作り、別 tenant の resource ID を意図的に使用します。Repository、cache、storage、queue、search、plugin bridge が cross-tenant access を拒否することを確認します。

結論

Z-CMS は trusted boundary で tenant context を決定し、immutable な context を Request → Authorization → Database → Cache → Storage → Queue → Search → Plugin → Theme → Logging 全体で enforce します。

Key takeaways

  • Security boundary は plugin の善意ではなく Core 側で enforce する必要があります。
  • Lifecycle、permission、tenant context、observability は同じ platform contract の一部です。
  • 優れた developer experience は、安全な runtime と supply chain の設計によって持続可能になります。

GitHub: https://github.com/zscontributor/z-cms
Website: https://z-cms.org