これは何?
SaaSアーキテクチャにおけるマルチテナンシーのデータ分離戦略です。
各テナント(顧客企業)のデータをどう管理するかを決めるモデルです。
3つのモデル
サイロモデル(Silo Model)
各テナントごとに完全に独立したインフラを用意します。
- データベース、サーバー、場合によってはAWSアカウントまで分離
- メリット: 最高レベルのセキュリティと分離、テナントごとのカスタマイズが容易
- デメリット: コスト高、運用管理が複雑
利用ケース:
- 規制業界(金融、医療)で物理的分離が必須
- 超大型エンタープライズ顧客(例: トヨタ、政府機関)
- 「専用環境」として販売する高額プラン
ブリッジモデル(Bridge Model)
インフラは共有するが、データベースレベルで分離する中間的なアプローチです。
パターン1: スキーマ分離
CREATE SCHEMA tenant_a;
CREATE TABLE tenant_a.users (...);
CREATE SCHEMA tenant_b;
CREATE TABLE tenant_b.users (...);
パターン2: テーブル分離
CREATE TABLE users_tenant_a (...);
CREATE TABLE users_tenant_b (...);
- メリット: サイロより安価、プールよりセキュア
- デメリット: スケーラビリティに限界がある
利用ケース:
- レガシーシステムからの移行過渡期
- 中途半端な分離要件(正直、あまり使われない)
- 技術的負債になりやすい
プールモデル(Pool Model)
すべてのテナントが同じテーブルを共有します。
-- テーブルは1つだけ
CREATE TABLE users (
id INT PRIMARY KEY,
tenant_id INT, -- これでテナントを識別
name VARCHAR(255),
email VARCHAR(255)
);
-- テナントAのデータ
INSERT INTO users VALUES (1, 100, 'Taku', 'taku@company-a.com');
-- テナントBのデータ
INSERT INTO users VALUES (2, 200, 'Hanako', 'hanako@company-b.com');
アプリケーション側で常にフィルタリング:
// テナントAのユーザーを取得
db.Where("tenant_id = ?", currentTenantID).Find(&users)
- メリット: 最もコスト効率が良い、スケールしやすい
- デメリット: アプリケーションレベルでの厳密なテナント分離が必須
重要: WHERE句でtenant_idを付け忘れると、全テナントのデータが見えてしまいます。
SaaSでよくある「他社のデータが見えちゃった事故」は、このモデルでの実装ミスが多いです。
staging/productionとの違い
staging/productionは環境の分離、マルチテナンシーは顧客の分離で、別の概念です。
production環境
├── tenant_a (A社のデータ) ← マルチテナンシーで分離
├── tenant_b (B社のデータ)
└── tenant_c (C社のデータ)
staging環境
├── tenant_a (A社のテストデータ)
└── tenant_b (B社のテストデータ)
どれを選ぶべきか
プールモデルが最も広く使われているアプローチです。
AWS、Microsoft、Googleのマルチテナンシードキュメントでも、プールモデルを基本的なアプローチとして紹介しています。
- Slack、Notion、GitHubなどの大規模SaaSがプールモデルを採用
- スタートアップは初期コストの観点からプールモデルで開始するケースが多い
- コスト効率とスケーラビリティに優れている
- 何千・何万テナント追加してもDB構造は変わらない
実際のハイブリッド戦略
多くのSaaSでは、以下のような使い分けをしています:
- 大多数の顧客: プールモデル
- 少数の大口顧客: サイロモデル
推奨アプローチ
まずはプールモデルで設計して、大口顧客が来たらサイロを検討するのが王道です。
プールモデルのセキュリティ対策
// ミドルウェアで強制的にテナントスコープを適用
func TenantScopeMiddleware(tenantID string) func(*gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
return db.Where("tenant_id = ?", tenantID)
}
}
// 使用例
db.Scopes(TenantScopeMiddleware(currentTenantID)).Find(&users)
このように、アプリケーション層でテナント分離を確実に実装することが重要です。
まとめ
- プールモデルが最も広く使われている
- サイロ・ブリッジは特殊ケース向け
- まずはプールモデルで設計することを推奨
- プールモデルではセキュリティ実装が最重要課題