Skip to content
··3分で読めます

エージェンシーが何を必要としているか分かっていた。だから2日目にマルチテナンシーを構築した

エージェンシーで20年の経験を経て、マルチテナントアーキテクチャは待てないと分かっていました。だから2日目に、動くAIエージェントが1つしかない状態で、将来のリライトを避けるために開発の複雑さを3倍にしました。

2025年8月21日。STRAŦUM構築の2日目。

1つの動くAIエージェント(Business Strategy)、基本的な認証、非常にシンプルなデータモデルを前にデスクに座っていました。コーヒーが冷めていきます。誰のために構築したいか正確に分かっていました:AIマーケティングの支援を必要とするSME、そして複数のクライアントにわたって使えるエージェンシーです。

両方のオーディエンス。1日目から。

エージェンシー側で20年間働いた後、エージェンシーが実際に何を必要としているかを理解していました — 光沢のあるマーケティングバージョンではなく、異なる戦略、異なるブランドガイドライン、異なるすべてを持つ10-15のクライアントを管理する混沌とした現実を。SMEが手頃な戦略的支援を切実に必要としていることも分かっていました。

しかし2日目、シンプルなデータモデルを見つめていて、不快なことに気づきました:両方のオーディエンスのために構築するということは、マルチテナントアーキテクチャを構築するということ。今すぐ。後ではなく。

その日の終わりまでに、開発の複雑さを3倍にし、タイムラインを数週間延長する決定にコミットしていました。

2日目からマルチテナントアーキテクチャを構築しました。

野心的だったのか、それとも正気ではなかったのか?正直、分かりませんでした。今でも100%確信はありません。しかし、ほとんど動くプロダクトがない状態で下したこの決定が、私が行った最も重要な技術的選択の1つになった理由の物語です。

なぜ両方のオーディエンス?20年のエージェンシー経験

ビジョンはSMEだけではありませんでした。最初から、2つの異なるオーディエンスのために構築したかったのです:

SME(中小企業、1-30人) — あなたが雇える戦略的共同創業者:

- マーケティングエージェンシーやコンサルタントを雇う余裕がない

- 戦略的マーケティングの専門知識がない

- 学習曲線なしのクイックウィンが必要

エージェンシー(5-15以上のクライアントを管理) — 戦略的キャパシティをスケール、人員数ではなく:

- 複数クライアントにまたがる戦略業務に溺れている

- スケールする一貫したフレームワークが必要

- クライアント間の完全なデータ分離が必須

なぜエージェンシーか?エージェンシー側で20年間過ごしたからです。ペインを身をもって知っています。

ストラテジストが12のクライアントを12の異なるブランドガイドラインで掛け持ちし、ツール間でコンテキストをコピー&ペーストし、間違った戦略を間違ったクライアントに送らないことを祈っているのを見てきました。優秀な人材が燃え尽きるのを見てきました。人員を増やさずに戦略的思考をスケールする方法がないからです。

そして正直なところ?自分自身に挑戦したかったのです。SMEだけなら簡単です — ユーザー1人、組織1つ、シンプルなルーティング。エージェンシーのために構築するということは、マルチクライアントダッシュボード、データ分離、ロールベースの権限、クライアントスコープのすべてが必要ということです。

複雑なものを構築したかった。20年間見てきた難しい問題を実際に解決するものを。

2日目の気づき:マルチテナンシーは後付けできない

2日目、シンプルなデータモデルを見つめていて理解したことはこうです:

SMEアーキテクチャ(当時持っていたもの):

```
users → campaigns → agent_outputs
```

マルチテナントアーキテクチャ(エージェンシーに必要なもの):

```
organizations (SME or AGENCY)
  ↓
clients (agencies only)
  ↓
campaigns
  ↓
agent_outputs (with org_id AND client_id)
```

違いは「数カラム追加する」程度ではありません。根本的に異なるデータモデルです。

SME用に先に構築してエージェンシーサポートを後で追加すると、以下が待っています:

- すべての既存テーブルにorg_idを追加するマイグレーション

- データベース全体にRLSポリシーを後付け

- クライアントコンテキスト用にフロントエンドルーティングを書き直し

- マルチテナントシナリオですべてを再テスト

それは機能追加ではありません。リライトです。

待てば待つほど、後付けは高くつきます。2日目は安かった。60日目は痛いでしょう。200日目は「エージェンシー向けに別のプロダクトを構築した方がいいかも」になります。

だから決断しました:2日目からマルチテナントを構築するか、エージェンシーが一級市民にならないことを受け入れるか。

トレードオフ:マルチテナンシーの本当のコスト

ここで一旦立ち止まって「本当にいいの?動くエージェントは1つしかないのに。1つだけ。」と自問すべきだったところです。

でも立ち止まりませんでした。代わりに、狂ったようにSQLスキーマを書き始めました。2日目のマルチテナンシーは、合理的な人なら再考するようなコストを受け入れることを意味しました:

コスト1:開発の複雑さ(3倍)

すべてのテーブルにorg_idが必要。エージェンシーテーブルにはclient_idも。RLSポリシーには両方のフィルターが。すべてのクエリ、すべてのAPIエンドポイント、すべてのフロントエンドルート — すべてがマルチテナント対応である必要がありました。

推定複雑さの増加:同じ機能に対して3倍の開発時間。

(ネタバレ:実際には4倍に近かったです。過小評価しました。典型的な私。 :P)

コスト2:開発タイムラインの延長

1日目から両方のオーディエンスのために構築するということは、ローンチまでの道のりが長くなるということです。こんな感じでした:

タイムライン

- 8月20日:構築開始(1日目からマルチテナント)

- 9月15日:4週間目、3エージェント動作中、10月を目標に

- 10月:10日間体調不良、コーディングできず

- 11月5日:Private Alphaローンチ(合計75日間)

マルチテナンシーが追加したもの

- publicとagencyテーブル間のスキーマルーティング

- すべてのテーブルのRLSポリシー(最終的に26テーブルに83個)

- すべてのエージェントを通じたクライアントコンテキストの伝播

- デュアルルーティングパターン(SME vs エージェンシーURL)

SME専用アーキテクチャならもっと早くローンチできたでしょうか?恐らく3-4週間早く。でもエージェンシーが来た時にリライトを見つめることになっていたでしょう。

コスト3:セキュリティ要件(リスク増大)

SMEのデータ分離は最低限。エージェンシーのデータ分離はミッションクリティカル。

追加された要件:

- 26テーブルにまたがる83のRLSポリシー

- スキーマレベルの分離(public vs agencyスキーマ)

- すべてのCRUD操作用のデータベースルーターファンクション

- 包括的な監査ログ

- 多要素認証オプション

セキュリティの複雑さ:SME専用より5倍高い。

ある週はAIエージェント開発よりもRow-Level Securityポリシーに多くの時間を費やしました。考えてみてください。

コスト4:テストの複雑さ(テストケース2倍)

すべての機能で以下のテストが必要:

- ✅ SMEフロー(シンプル)

- ✅ エージェンシーフロー(クライアントスコープ)

- ✅ データ分離(Nike ≠ Adidas)

- ✅ 権限境界(エージェンシーはSMEデータを見れるか?いいえ。)

テストメンテナンス:倍増。

同じテストを少し違うコンテキストで2回書くのが楽しいか知っていますか?誰も楽しくないです。

---

それでもトレードオフを受け入れた理由

つまり:3倍の複雑さ、延長されたタイムライン、5倍のセキュリティオーバーヘッド、倍増したテスト...それでもやったのか?

はい。考え方はこうです(多少なりとも):

理由1:後付けは悪夢

2日目:ほぼコードが書かれていない。データモデルはまだ柔軟。移行すべきユーザーなし。

60日目:45テーブル、20万行のコード、15人のアルファユーザー。マルチテナンシーの追加はコードベースの半分を書き直す必要がある。

教訓:アーキテクチャの決定は時間とともに変更が指数関数的に難しくなる。

2日目のコスト:データモデルの再設計に約5時間。

60日目のコスト:すべてのリファクタリングに約200時間。

マルチテナンシーを後付けしようとした企業を見てきました。残酷です。飛行機を飛ばしながら再構築するようなものです。乗客がいて。お金を払ってくれている乗客が。

理由2:エージェンシーを実際に理解している

これは理論的な市場調査ではありませんでした。20年間、まさにこの問題に苦しむエージェンシーを見てきました。

必要なものを知っていました:

- 完全なデータ分離(クライアント1はクライアント2の戦略を見れない)

- クライアントスコープのすべて(ブランドガイドライン、ペルソナ、キャンペーン)

- ロールベースのアクセス(アカウントマネージャー vs ストラテジスト)

- クライアントプレゼンテーション用のホワイトラベルの可能性

SME専用でリリースして「エージェンシーは後で追加」では無駄にしたくない優位性でした。ペインを生きてきたから正しく構築できるのです。

理由3:チャレンジがポイントだった

あまり言わないことがあります:複雑さが_欲しかった_のです。

私は家族持ちの中年ファウンダーで、エナジードリンクと睡眠なしで生き延びられる22歳ではありません。1年かけて何かを構築するなら、チャレンジングなものでなければ。技術的に誇りに思えるものでなければ。

適切なデータ分離、RLSポリシー、スキーマルーティングを持つマルチテナントアーキテクチャ — それは難しい。面白い。深夜2時まで夢中になれる種類のエンジニアリング問題です。

SME専用ならもっと速かったでしょう。でも難しい部分を乗り越えるモチベーションがあったでしょうか?恐らくなかったでしょう。

理由4:両方のオーディエンスが互いをより良くする

SMEとエージェンシーは競合するユースケースではありません。補完的です:

- エージェンシーがプロダクトを検証:エージェンシーがクライアントデータを信頼するなら、SMEもそうするでしょう

- SMEがボリュームを提供:より多くのユーザーはより多くのフィードバック、より速いイテレーション

- アーキテクチャが両方に対応:チームアカウント、ロール権限、ホワイトラベルは両方のオーディエンスに機能

両方のために構築するということは、すぐに成長を超えてしまう簡略版ではなく、_完全な_ソリューションを構築するということでした。

理由5:将来への備え

エージェンシー以外にも、マルチテナンシーアーキテクチャは以下を可能にしました:

- チームアカウント(複数ユーザーのSME)

- リセラーパートナーシップ

- ホワイトラベルの機会

- エンタープライズセールス(もしそうしたいなら)

オプション価値:マルチテナンシーは2日目には想像もできなかったビジネスモデルを解放しました。

実装:2日目のアーキテクチャ決定の実際

8月21日に実際に構築したものがこちらです:

データベーススキーマ(コア決定)

```sql
-- Organizationsテーブル(SME or AGENCY)
CREATE TABLE organizations (
  id UUID PRIMARY KEY,
  name TEXT,
  type TEXT CHECK (type IN ('SME', 'AGENCY')),  -- 重要な区別
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Clientsテーブル(エージェンシー専用)
CREATE TABLE clients (
  id UUID PRIMARY KEY,
  org_id UUID REFERENCES organizations(id),  -- どのエージェンシーが所有
  company_name TEXT,
  slug TEXT UNIQUE,  -- URLルーティング用
  created_at TIMESTAMPTZ DEFAULT NOW(),

  -- 制約:エージェンシーのみがクライアントを持てる
  CONSTRAINT clients_agency_only CHECK (
    (SELECT type FROM organizations WHERE id = org_id) = 'AGENCY'
  )
);

-- Campaignsテーブル(マルチテナント対応)
CREATE TABLE campaigns (
  id UUID PRIMARY KEY,
  org_id UUID REFERENCES organizations(id),  -- 常に必須
  client_id UUID REFERENCES clients(id),      -- エージェンシーは必須、SMEはnull
  name TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Agent outputs(マルチテナント + クライアントスコープ)
CREATE TABLE agent_outputs (
  id UUID PRIMARY KEY,
  org_id UUID REFERENCES organizations(id),
  client_id UUID REFERENCES clients(id),
  campaign_id UUID REFERENCES campaigns(id),
  agent_type TEXT,
  content JSONB,
  created_at TIMESTAMPTZ DEFAULT NOW()
);
```

2日目の重要な決定

- すべてのテーブルにorg_id(RLSを可能にする)

- client_idはnullable(SMEにはクライアントがない)

- organizationsにtypeディスクリミネーター(SME vs AGENCY)

- 制約:エージェンシーのみがクライアントを作成可能

Row-Level Security(2日目の計画)

```sql
-- Campaignsに対するRLSポリシー(マルチテナント分離)
CREATE POLICY campaigns_org_isolation ON campaigns
  FOR ALL TO authenticated
  USING (
    org_id = get_user_org_id() AND
    (client_id IS NULL OR client_id IN (SELECT id FROM clients WHERE org_id = get_user_org_id()))
  );
```

このポリシーは以下を保証します:

- ✅ ユーザーは自分の組織のキャンペーンのみ表示

- ✅ エージェンシーユーザーはSMEキャンペーンを見れない

- ✅ エージェンシーは互いのクライアントを見れない

コスト:2日目に5つの初期RLSポリシーを作成。最終的にローンチまでに83に成長。

URLルーティング(1日目からマルチテナント)

```typescript
// フロントエンドルーティング構造(2日目に計画)

// SMEルート(シンプル)
/campaigns/:campaignId
/agents/strategy/:campaignId

// エージェンシールート(クライアントスコープ)
/clients/:clientSlug/campaigns/:campaignId
/clients/:clientSlug/agents/strategy/:campaignId
```

このURL構造は以下を可能にしました:

- ✅ すべてのURLにクライアントコンテキスト(エージェンシー)

- ✅ SMEとエージェンシーフローの明確な分離

- ✅ ブックマーク可能、共有可能なURL

- ✅ クライアントごとのアナリティクス追跡

結果:割に合ったのか?

ローンチ日:2025年11月5日(開始から75日目)

マルチテナンシーによる開発オーバーヘッド

- スキーマ設計とマイグレーション

- 26テーブルにまたがる83のRLSポリシー

- データベースルーターファンクション

- デュアルルーティングパターン(SME vs エージェンシーURL)

- クライアントコンテキストの伝播

Claude CodeとGemini 2.5 Proのおかげで、実際のコーディングは一人でやるより速く進みました。でも_動くはず_なのに動かないRLSポリシーのデバッグ?AI支援があっても深夜2時にそれは辛いです。

今あるもの

- 組織間の完全なデータ分離

- クライアントスコーピングを持つエージェンシー対応アーキテクチャ

- 26テーブルにまたがる83のRLSポリシー

- スキーマレベルルーティング(public vs agencyスキーマ)

- 1日目から組み込まれたホワイトラベルの可能性

- SMEとエージェンシーの両方に真に対応できるプロダクト

割に合ったか?

実際にお金を払ってくれる顧客ができてから6ヶ月後に聞いてください。今はPrivate Alphaで15人以上のユーザーがいて、何が効いて何が効かないかまだ学んでいます。

でも分かっていること:両方のオーディエンスに適切に対応するアーキテクチャがあるということです。SME専用で構築してエージェンシーを後で追加しようとしていたら、今頃3ヶ月のリライトを見つめていたでしょう。代わりに、プロダクトマーケットフィットに集中できています。アーキテクチャの負債ではなく。

正しいトレードオフだったと感じています。

フレームワーク:マルチテナンシーを早期に構築すべき時

すべてのプロダクトが2日目にマルチテナンシーを必要とするわけではありません。このフレームワークを使ってください:

早期にマルチテナンシーを構築すべき場合:

ターゲット市場にエージェンシー/リセラーが含まれる(顧客あたりの価値が高い)

データ分離が重要(法的/コンプライアンス要件)

チームアカウントを持つB2Bの可能性が高い(Slack、Figmaモデル)

ホワイトラベルの機会がある(リセラーGTM戦略)

エンタープライズセールスの可能性(1日目からマルチクライアントが必要)

早期のマルチテナンシーをスキップすべき場合:

B2Cプロダクト(個人ユーザー、組織階層なし)

MVPにスピードが必要(まずプロダクトマーケットフィットをテスト)

チーム/エージェンシーのユースケースなし(シングルプレイヤーツール)

シンプルなデータモデル(後でorg_idを追加するのが簡単)

学習中のソロファウンダー(複雑さが多すぎるかも)

教訓:自分の専門性のために構築する

1. オーディエンスの知識がアーキテクチャを駆動すべき

仮想のユーザーではなく、実際に理解しているユーザーのために設計してください。

私の優位性:20年のエージェンシー経験により、エージェンシーが何を必要としているか正確に分かっていた — データ分離、クライアントスコーピング、ロールベースアクセス。

間違ったアプローチ:「まずSME向けに構築して、理解してからエージェンシーを追加しよう」

正しいアプローチ:「エージェンシーは既に理解している。安いうちに今構築しよう。」

マーケットセグメントに深い専門知識があるなら、それを活かしてください。「シンプルなものから」に先送りしないでください。

2. 早期の複雑さは遅いリファクタリングに勝つ

2日目のアーキテクチャコスト:データモデルの再設計に5時間。

60日目のアーキテクチャコスト:すべてのリファクタリングに200時間。

40倍の差。

マルチテナンシーが必要になると80%確信しているなら、早期に構築してください。待てば待つほど高くつきます。

3. 技術的チャレンジはモチベーションになる

これは個人的なことです:難しいものを構築_したかった_のです。

RLSポリシー、スキーマルーティング、適切なデータ分離を持つマルチテナントアーキテクチャ — 面白いエンジニアリングです。難しい部分を乗り越えるモチベーションを維持してくれました。

ソロファウンダーなら、モチベーションを過小評価しないでください。チャレンジングなものを構築することは、速くリリースすること以上に価値があるかもしれません。

4. アーキテクチャがビジネスモデルを可能にする

マルチテナンシーは技術的な決定だけではありませんでした。以下を可能にしました:

- エージェンシー価格帯(価格設定を考えたら)

- ホワイトラベルの機会

- エンタープライズセールスの可能性

- SME向けのチームアカウント

アーキテクチャ = コード形式のビジネスの選択肢。

5. ソロファウンダーもマルチテナントを構築できる

「マルチテナンシーは一人には複雑すぎる」→ 間違い。

最新ツール(Supabase RLS、データベースファンクション、AI支援開発)を使えば、ソロファウンダーもエンタープライズグレードのマルチテナンシーを構築できます。

時間コスト:70日間(パートタイム)。

得られるもの:後で後悔するハックではなく、本物のデータ分離。

両方のオーディエンスに真剣なら価値あり

最後の考え

2日目はSTRAŦUMが成功するかどうかを知るには早すぎました。エージェントは1つ。ユーザーなし。バリデーションなし。ただ20年のエージェンシー経験がエージェンシーが実際に何を必要としているかを教えてくれていただけです。

延長されたタイムラインは時に辛かったです。RLSポリシーのデバッグやデータベースルーターファンクションの作成をしている間に他のローンチを見ていましたか?フラストレーションどころではありません。複雑さに見合う価値があるのか疑問に思った日もありました。

でも今分かっていること:1日目から両方のオーディエンスのために構築したのは正しい判断でした。巧みな単位経済計算のためではありません — どの価格設定が効くかまだ分かりません。でも、両方のオーディエンスに適切に対応できるアーキテクチャがあるからです。

2日目のマルチテナンシーは技術的な決定だけではありませんでした。後で考えるつもりの仮想のユーザーではなく、実際に理解しているユーザーのために構築するという賭けでした。

その賭けは割に合ったか?

Private Alphaで15人以上のユーザーがいます。まだ答えは出ていません。でも言えることは:エージェンシーが「複数のクライアントで適切なデータ分離付きで使えますか?」と問い合わせた時、「はい」と言えるということです。「それは開発中です」と言う必要はありません。

それが正しい基盤だと感じています。

野心的だったのか、ただ頑固だったのか、時間が教えてくれるでしょう。(正直なところ、両方少しずつでしょう。) :)

その時は野心的すぎると感じたアーキテクチャの賭けをしたことはありますか — そしてそれは割に合いましたか?あなたの話を聞かせてください。

よろしくお願いします、Chandler

STRAŦUMアーキテクチャシリーズ: この2日目の決定は始まりに過ぎませんでした。67日目にセキュリティ監査がアーキテクチャの欠陥を明らかにし、マルチテナンシーレイヤー全体を再構築しなければなりませんでした。そしてナビゲーションコンテキストの喪失による31の空白画面が続き、データベースが技術的には正しいが296倍遅いことが判明しました。

---

マルチテナントSaaSを構築していますか? STRAŦUMのアーキテクチャは1日目からSMEとエージェンシー両方の顧客をサポートしています。https://stratum.chandlernguyen.com/request-invitationでアルファアクセスをリクエスト

---

アーキテクチャの決定がビジネスの決定の変装だと学び続けています。まだRLSポリシーをデバッグしています。まだ2日目の選択を疑問視しています(でも以前ほどではなくなりました)。 https://www.chandlernguyen.com/ でもっと構築の冒険を。

---

続きを読む

私の歩み
つながる
言語
設定