Talentera(Bayt.comのB2B採用プラットフォーム)で、一つのコードベースで企業と 政府機関にサービスを提供するマルチテナントワークフローを設計するのに数年を費やした。 データモデルは初日に問題なく見えた — そして、より驚くべきことに — 何年もの テナント固有の要求を乗り越えた。コツはアイデンティティとアクセスコントロールを ビジネスロジックが書かれた後に後付けしたミドルウェアの懸念としてではなく、 ファーストクラスの設計軸として扱うことだった。この記事は2026年にマルチテナント プラットフォームを構築するエンジニア全員に推奨することの短いバージョンだ。
問題の形
マルチテナントな採用はアクセスコントロールにとってほぼ最悪のケースだ:
- 複数クラスのテナント。 企業、中小企業、政府省庁 — 同じプラットフォーム、 「隔離」が何を意味するかについての全く異なる期待。
- 共有スタッフ。 今四半期はあるエージェンシーで、来四半期は別のエージェンシーで 働く採用担当者。彼らの履歴はリークしてはいけない。クレデンシャルはポータブルであるべきだ。
- 候補者はクロステナントのプリミティブだ。 候補者は十社に応募する。それらの 企業はお互いのその候補者とのインタラクションを見るべきではない。
- 政府契約は監査要件を伴う。 「ログを記録している」ではない。定期的な監査、 ミスに対する法的結果を伴う正式な要件だ。
これらの箇条書きのすべてはまずアイデンティティ/アクセスコントロールの問題だ。 残りのプラットフォームはその上の薄い層だ。
機能したモデル
出荷されて生き残ったモデルは四つのプリミティブを中心に構築された:
1. テナントをファーストクラスのnullableでないエンティティとして
データベースのすべての行にテナントカラムがあり、データベースレベルの制約で 強制されていた。アプリレベルのフィルターではない。「追加することを覚えておく」でもない。 Postgresのrow-level security(または他のストアの同等物)。エンジニアがテナント フィルターを含まないクエリを書いた場合、データベースがエラーを発生させた。
トレードオフはすべてのクエリがわずかに冗長に見えることだった。勝利はアプリの バグがクロステナントのリークを引き起こすことができないことだった。データベースが リクエストを提供することを拒否するから。
2. ユーザー/ロール/パーミッションではなく、主体/リレーションシップ/リソース
既製品のモデル — ユーザーはロールを持ち、ロールはパーミッションを持ち、パーミッションは リソースを保護する — シンプルなアプリには良い。マルチテナントな採用コンテキストでは 壊れる。なぜならユーザーのロールは彼らがどのテナントで操作しているかに依存するから。 エージェンシーAで「admin」の採用担当者は、エージェンシーBで「guest」で、エージェンシーCでは 「nothing」だ。
私たちが使ったモデルはZanzibarスタイル(ZanzibarがエンジニアリングコミュニティでHouseholdな 名前になる前)に近かった:主体はリソースへのリレーションシップを持ち、現在のテナント コンテキストで調停される。単一のクエリが「この主体はこのコンテキストでこのリソースに 対してこのアクションをできるか?」に答え、その答えはロール列挙型ではなくリレーションシップ グラフから計算された。
その単一の変更が、ロール列挙型モデルではカスタムパーミッションコードを必要とした だろう数十のプロダクトフィーチャーをアンロックした。
3. 共有アイデンティティ、スコープされたセッション
ユーザーは一つのアイデンティティを持った — ログイン。認証すると、特定のテナントに スコープされたセッショントークンを得た。テナントの切り替えは既存のものを変異させることでは なく、新しいセッションの発行を意味した。セッションはテナントID、そのテナントに 関連する主体のリレーションシップ、そしてセッション自身の監査可視な識別子を持った。
これは、すべての監査されたアクションが(主体、テナント、セッション、タイムスタンプ) まで簡単に追跡可能であることを意味した — 監査人からの「誰が誰のために何をしたか」の 問いが一クエリの答えになった。
4. 追記専用のドメインイベントとしての監査
システムのすべての変異が追記専用の監査ログにドメインイベントをエミットした。監査ログは アプリケーションスキーマのファーストクラスの部分だった、運用の後付けではなく。 政府の監査人が「この採用担当者による3月のすべての履歴書閲覧を見せろ」と言ったとき、 答えはクエリだった、複数週間のログ相関演習ではなかった。
これを初日から構築すると、後付けするより三倍安い。両方やったから知っている。
失敗したこと
二つの間違い:
1. ロール継承の最初のバージョンは平すぎた。 admin-user-contributorの階層が あるだけだった。企業はサブ管理者を欲しがった。後付けでロールコンポジションを追加しなければ ならなかった。つまり「adminはこれらすべてを意味する」という仮定を解きほぐすことを意味した。 正しいアクションは初日からコンポジションを構築することだっただろう — 顧客20番目まで 必要性を見なかったが。
2. マルチテナントモデルは分析にクリーンに拡張しなかった。 トランザクショナル クエリに対しては、行レベルのテナントスコープは確実だった。BI/レポートのワークロードには、 すぐに高くなった。クエリレベルではなくパイプラインレベルでテナント境界を強制する 別の分析パイプラインになった。それは機能した — しかし構築するための多くの配管を 二回作ることになり、もう一度やるなら違う方法でやる。
2026年に違うやり方
四年後、一から始めるなら:
- **OPA(Open Policy Agent)**または類似のポリシーエンジンに初日から手を伸ばす、 自分でパーミッション評価エンジンを構築するのではなく。より安く、監査済み、 十分テスト済みで、概念モデルは同じだ。
- Postgresのrow-level securityまたは同等の硬い境界を使う。「アプリでフィルターする」 ではない。境界はデータベースレイヤーになければならない。
- 最初のミューテーションエンドポイントを書く前に構造化された監査イベントスキーマを 選ぶ。 すべてのイベントは(actor、subject、action、resource、tenant、timestamp、outcome、 diff)だ。新しいフィーチャーがスキーマがサポートしないイベント形状を必要とするなら、 それはスキーマの会話であり、「何かをログする」ではない。
- DIYの認証よりもOpen ID Connect + 専用のアイデンティティプロバイダーを使う。 Pocket ID、Keycloak、Auth0、Ory — 良いオプションはDIYコストより安い。
一般化するパターン
これは採用固有ではない。すべてのB2B SaaS、すべてのマルチテナントプラットフォーム、 すべての政府隣接システムは同じ形を持つ。ほとんどがぐちゃぐちゃな理由は、アイデンティティ とアクセスコントロールがミドルウェアとして扱われるからだ — 後付けで、仕様不足で、 決して優先されない。長く持つものはアイデンティティをプロダクトの次元として扱う: ドメインモデルの一部、設計レビューの一部、ロードマップの一部として。
今マルチテナントプラットフォームを構築しているなら:アイデンティティモデルは 最初の一年で行う最も荷重の高い設計の選択だ。 後付けで安くできない。正しくやれ。 時間を使え。Zanzibarの論文を読め。やった人と話せ。複利で効く。