SaaSプロダクトを作る場合: マルチテナント・課金・認証・API設計
はじめに
SaaS(Software as a Service)は、クラウド経由でソフトウェアを提供するビジネスモデルです。Slack、Notion、Figmaなどが代表例です。SaaSの技術的な特徴は、複数の企業(テナント)が同じシステムを共有して利用する「マルチテナント」アーキテクチャにあります。
身近な例えで理解するSaaS
SaaSは「賃貸マンション」のようなものです。
- マルチテナント = マンション。複数の世帯(企業)が同じ建物(システム)に住んでいるが、各部屋(データ)は完全に隔離されている
- 課金 = 家賃。月額いくら、何人で住むか(シート数)で料金が変わる
- 認証 = エントランスのセキュリティ。鍵(パスワード)やカードキー(SSO)で入館管理
- API = 郵便受け。外部のサービス(郵便)とデータをやり取りするインターフェース
全体アーキテクチャ
推奨構成: Next.js + NestJS + PostgreSQL + Stripe
+-------------------+
| CloudFront |
| (CDN + WAF) |
+--------+----------+
|
+--------v----------+
| Next.js |
| (Frontend) |
| on Vercel |
+--------+----------+
|
+--------v----------+
| API Gateway |
| (認証/レート |
| リミット) |
+--------+----------+
|
+--------------+--------------+
| | |
+-------v------+ +----v-------+ +----v--------+
|Core API | |Auth | |Billing |
|NestJS | |Service | |Service |
|(ビジネス | |(認証/認可) | |(課金/請求) |
| ロジック) | | | | |
+-------+------+ +----+-------+ +----+--------+
| | |
+------v------+ +---v--------+ +---v--------+
|PostgreSQL | |Auth0 / | |Stripe |
|(Aurora) | |Cognito | |Billing |
|テナント | +------------+ +------------+
|分離対応 |
+------+------+
|
+------v------+
|Redis |
|(ElastiCache) |
|セッション/ |
|キャッシュ |
+-------------+
+-------------------------------------------+
| バックグラウンド処理 |
| - SQS: 非同期ジョブ |
| - Lambda: Webhook処理、通知 |
| - EventBridge: スケジュールタスク |
| - S3: ファイルアップロード |
+-------------------------------------------+
マルチテナントアーキテクチャ
3つのマルチテナント方式
| 方式 | 説明 | データ分離 | コスト | 運用複雑度 |
|---|
| DB分離 | テナントごとに別DB | 最高 | 高い | 高い |
| スキーマ分離 | テナントごとに別スキーマ | 高い | 中 | 中 |
| 行レベル分離(RLS) | 全テナント同一テーブル、行で分離 | 中 | 低い | 低い |
方式の詳細比較
【DB分離】
Tenant A → Database A (PostgreSQL)
Tenant B → Database B (PostgreSQL)
Tenant C → Database C (PostgreSQL)
メリット: 完全なデータ分離、テナント別のバックアップ/復旧
デメリット: テナント数に比例してDBコストが増加、マイグレーション管理が大変
【スキーマ分離】
Database (PostgreSQL)
├── Schema: tenant_a
│ ├── users
│ ├── projects
│ └── ...
├── Schema: tenant_b
│ ├── users
│ ├── projects
│ └── ...
└── Schema: shared (共通データ)
├── plans
└── features
メリット: 良好なデータ分離、1つのDBで管理可能
デメリット: スキーマ数が増えるとパフォーマンス低下
【行レベル分離(RLS)】
Database (PostgreSQL)
└── Table: users
├── id=1, tenant_id='A', name='田中'
├── id=2, tenant_id='A', name='鈴木'
├── id=3, tenant_id='B', name='佐藤'
└── id=4, tenant_id='B', name='山田'
メリット: シンプル、コスト最小、スケーラブル
デメリット: SQLミスでデータ漏洩のリスク(RLSで軽減)
推奨: 行レベル分離(RLS)
PostgreSQLのRLS(Row Level Security)を使うことで、行レベルの分離をDBレベルで強制できる。
-- RLSの設定例
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON projects
USING (tenant_id = current_setting('app.current_tenant')::uuid);
認証・認可
認証方式の比較
| 方式 | 説明 | 適したケース |
|---|
| メール + パスワード | 最も基本的な認証 | 全般 |
| SSO(SAML/OIDC) | 企業のIdPと連携 | エンタープライズ |
| ソーシャルログイン | Google、GitHub等 | スタートアップ |
| マジックリンク | メールでログインURL送信 | パスワードレス |
認証サービスの比較
| サービス | 料金(月額) | SSO対応 | 特徴 |
|---|
| Auth0 | 無料〜$23/1000MAU | 対応 | 最も機能が充実 |
| Clerk | 無料〜$25/1000MAU | 対応 | DX最高、React統合が優秀 |
| AWS Cognito | 無料〜$0.0055/MAU | 対応 | AWS統合、安い |
| Supabase Auth | 無料〜$25/月 | 対応 | PostgreSQL統合 |
| Firebase Auth | 無料〜$0.06/MAU | 対応 | Google統合 |
推奨: Clerk or Auth0
- スタートアップ: Clerk(DXが最高、React/Next.jsとの統合が簡単)
- エンタープライズ: Auth0(SSO、MFA、コンプライアンス対応が充実)
RBAC(Role-Based Access Control)
| ロール | 権限 |
|---|
| Owner | 全権限 + 請求管理 + テナント削除 |
| Admin | メンバー管理 + 設定変更 + 全データアクセス |
| Member | データの作成/編集/閲覧 |
| Viewer | データの閲覧のみ |
| Guest | 招待されたプロジェクトのみアクセス |
課金・請求
課金モデルの比較
| モデル | 説明 | 例 |
|---|
| フラットレート | 固定月額 | Basecamp |
| シートベース | ユーザー数 x 単価 | Slack、Notion |
| 使用量ベース | 使った分だけ | AWS、Twilio |
| ティアベース | プランごとに固定料金 | GitHub、Figma |
| ハイブリッド | 基本料金 + 使用量 | Vercel |
Stripe Billingの活用
[課金フローの全体像]
1. プラン選択
フロント → Stripe Checkout → 決済完了
2. Webhook受信
Stripe → Webhook → NestJS → DB更新
3. 月次請求
Stripe(自動課金) → Webhook → DB更新 → メール通知
4. プラン変更
フロント → Stripe API → 比例配分(proration)計算 → 請求調整
5. 解約
フロント → Stripe API → 期間終了時にアクセス停止
Stripe Billingの主要概念
| 概念 | 説明 |
|---|
| Customer | 課金対象の顧客(テナント) |
| Subscription | 定期課金の設定 |
| Product | プラン(Free、Pro、Enterprise等) |
| Price | 価格設定(月額$10、年額$100等) |
| Invoice | 請求書 |
| Payment Method | 支払い方法(クレジットカード等) |
| Webhook | 決済イベントの通知 |
API設計
REST vs GraphQL
| 項目 | REST | GraphQL |
|---|
| エンドポイント | リソースごとに複数 | 単一エンドポイント |
| データ取得量 | 固定(over-fetching/under-fetching) | クライアントが指定(必要な分だけ) |
| キャッシュ | HTTP標準キャッシュ | 専用キャッシュ必要 |
| 学習コスト | 低い | 中 |
| ドキュメント | Swagger/OpenAPI | GraphQL Playground |
| リアルタイム | 別途WebSocket | Subscription対応 |
推奨: REST API(NestJS + Swagger)
SaaSのAPIは外部公開する可能性が高いため、最も広く理解されているRESTが無難。GraphQLはフロントエンド内部での利用に限定するか、BFF(Backend For Frontend)パターンで活用。
APIバージョニング
| 方式 | 例 | メリット | デメリット |
|---|
| URLパス | /api/v1/users | 分かりやすい | URLが変わる |
| ヘッダー | Api-Version: 2 | URLが変わらない | 見えにくい |
| クエリパラメータ | /api/users?version=1 | シンプル | キャッシュに影響 |
推奨: URLパス方式(/api/v1/...)
レートリミット
| プラン | リクエスト上限 | バースト上限 |
|---|
| Free | 100リクエスト/分 | 10リクエスト/秒 |
| Pro | 1,000リクエスト/分 | 50リクエスト/秒 |
| Enterprise | 10,000リクエスト/分 | 200リクエスト/秒 |
実装方法: Redis + Sliding Window方式
チーム規模と開発スケジュール
MVP(3ヶ月)
| 役割 | 人数 | 担当範囲 |
|---|
| フロントエンド | 2人 | ダッシュボード、設定画面 |
| バックエンド | 2人 | API、認証、課金連携 |
| インフラ | 1人 | AWS構築、CI/CD |
| デザイン | 1人 | UI/UXデザイン |
| PM | 1人 | 進捗管理、要件定義 |
| 合計 | 7人 | 3ヶ月 |
MVPの機能:
- テナント作成/管理
- ユーザー認証(メール + Google)
- RBAC(Owner、Admin、Member)
- コア機能(プロダクト固有)
- Stripe課金(Free、Proプラン)
- ダッシュボード
本格展開(追加6ヶ月)
| 追加機能 | 必要人数 | 期間 |
|---|
| SSO(SAML/OIDC) | 1人 | 2ヶ月 |
| 外部API公開 | 1人 | 2ヶ月 |
| 監査ログ | 1人 | 1ヶ月 |
| Webhook配信 | 1人 | 1ヶ月 |
| レポート/分析 | 2人 | 3ヶ月 |
| モバイル対応 | 2人 | 4ヶ月 |
コスト見積り
MVPフェーズ(〜100テナント、〜1,000ユーザー)
| サービス | 構成 | 月額 |
|---|
| Vercel | Proプラン | $20 |
| ECS/Fargate | 2タスク | $40 |
| Aurora PostgreSQL | db.t3.medium | $130 |
| ElastiCache Redis | cache.t3.micro | $25 |
| Auth0/Clerk | 無料枠内 | $0 |
| Stripe | 手数料のみ | 変動 |
| S3 | 10GB | $1 |
| 合計 | | 約$216/月 |
成長フェーズ(〜1,000テナント、〜10,000ユーザー)
| サービス | 構成 | 月額 |
|---|
| Vercel | Teamプラン | $100 |
| ECS/Fargate | 4タスク x 1vCPU | $200 |
| Aurora PostgreSQL | db.r6g.large + Replica | $600 |
| ElastiCache Redis | cache.r6g.large | $200 |
| Auth0 | Professionalプラン | $240 |
| Stripe | 手数料のみ | 変動 |
| S3 + CloudFront | 100GB | $15 |
| SQS/Lambda | 非同期処理 | $10 |
| 合計 | | 約$1,365/月 |
エンタープライズ対応
エンタープライズ顧客を獲得するために必要な機能:
| 機能 | 説明 | 実装優先度 |
|---|
| SSO(SAML) | 企業のIdPと連携 | 高 |
| SCIM | ユーザーの自動プロビジョニング | 中 |
| 監査ログ | 全操作の記録 | 高 |
| データエクスポート | CSV/JSON形式でデータ出力 | 中 |
| SLA | 99.9%以上の可用性保証 | 高 |
| SOC2/ISO27001 | セキュリティ認証 | 中〜高 |
| カスタム契約 | 年間契約、請求書払い | 高 |
| 専任サポート | テクニカルアカウントマネージャー | 中 |
| データ所在地 | 特定リージョンでのデータ保存 | 中 |
| IP制限 | 特定IPからのみアクセス許可 | 低〜中 |
セキュリティのベストプラクティス
| 対策 | 実装方法 |
|---|
| テナント間データ分離 | PostgreSQL RLS |
| 認証 | Auth0/Clerk + MFA |
| APIセキュリティ | レートリミット、CORS、APIキー |
| データ暗号化 | 通信: TLS、保存: AES-256 |
| 脆弱性スキャン | Snyk、Dependabot |
| WAF | AWS WAF(SQLインジェクション、XSS防御) |
| 監査ログ | 全API呼び出しのログ記録 |
| ペネトレーションテスト | 年1回以上の外部テスト |
まとめ
推奨技術スタック
| レイヤー | 技術 | 理由 |
|---|
| フロントエンド | Next.js | SSR、SEO、React エコシステム |
| バックエンド | NestJS | TypeScript、モジュール構造、API設計 |
| データベース | PostgreSQL (Aurora) | RLS、信頼性 |
| キャッシュ | Redis | セッション、レートリミット |
| 認証 | Clerk or Auth0 | SSO、MFA、RBAC |
| 課金 | Stripe Billing | サブスクリプション管理 |
| API | REST + OpenAPI | 標準的、外部公開しやすい |
| インフラ | AWS (ECS/Fargate) | スケーラブル |
SaaSプロダクトは技術的な完成度だけでなく、「顧客が安心して使えるか」が重要。セキュリティ、可用性、サポート体制の3つを軸に技術選定を行うことが成功のカギである。
参考リンク