組織、チーム、テナント分離
顧客の共同作業モデルを決め、同梱の組織内の役割、招待、利用人数、テナント境界を調整します。
スターターのコミット
2a1a04aで検証済みです。
最初にプロダクト上の問いに答えます。あなたの SaaS は、一人で使う人、共同で使うチーム、複数の組織を管理する人のうち、誰に販売しますか。Sushi SaaS には「一人から始めてチームへ成長できる」モデルが含まれています。各顧客は個人用の組織から始まり、課金対象のリソースはすべて組織に属します。
同梱の共同作業モデル
| 動作 | 既定 | 将来の選択肢 |
|---|---|---|
| リソースの所有 | 組織内で共有 | can() に、作成者だけが扱える規則またはリソース単位の共有を追加 |
| 役割 | owner、admin、member | 製品固有の役割または Better Auth の動的アクセス制御を追加 |
| 課金 | 組織ごとの固定価格 | 利用人数に応じた数量と、検証済みの日割り計算を追加 |
| ナビゲーション | ?org=<slug> を使うワークスペース切替 | URL に強い階層が必要なら組織別のパスを追加 |
| チーム | 組織への所属と招待 | 下位グループが必要な場合だけ、入れ子のチームを有効化 |
すべてのアカウントに組織がある
新規登録時に個人用の組織と owner の所属記録を作成します。ユーザー所有リソースという別経路はなく、ファイル、タスク、注文、契約、クレジット、予約は org_uuid を使います。
Better Auth がメンバーシップと招待を提供し、Sushi がテナント範囲、権限、席数制限、共有残高、同時実行ガードを追加します。
ロール
階層は owner ⊃ admin ⊃ member です。
| 操作 | Member | Admin | Owner |
|---|---|---|---|
| データ閲覧、ファイル作成/削除、クレジット消費 | ✓ | ✓ | ✓ |
| 招待、削除、ロール変更 | ✓ | ✓ | |
| 組織更新 | ✓ | ✓ | |
| 課金管理、組織削除 | ✓ |
getOrgContext(request, optionalSlug) で利用者、組織、役割を解決し、can(ctx, action, resource) で認可します。リクエスト本文の org_uuid は信用しません。
テナント分離と席数
テナントに属する表のクエリは、モデル内で scopedToOrg(column, ctx.orgUuid) を使います。アーキテクチャテストが、組織を限定していない読み書きを検出します。
招待は 72 時間で期限切れになり、招待先メールに紐づき、有効な間は席を予約します。
| プラン | 総席数 |
|---|---|
| Free | 1 |
| Plus | 5 |
| Max | 20 |
招待の送信時と承認時の両方で、同じ組織単位の PostgreSQL アドバイザリーロックの下で上限を確認します。管理画面から、監査記録付きの一時的または無期限の例外も設定できます。
不変条件
- 最後の
ownerは削除できません。 - 唯一の組織から退出できません。
- 自分より上のロールを付与できません。
- ダウングレードは既存メンバーやデータを削除せず、新規追加だけを止めます。
- クレジットと Stripe Customer は組織に属します。
画面は /{locale}/account/team、API は /api/account/team/* です。クエリパラメーター式の組織切り替えは含まれていますが、組織スラッグを含むルート、独自の役割、入れ子のチーム、Stripe の利用人数課金はまだありません。
変更前に、顧客データの所有者、招待・削除できる人、member が共有コンテンツを削除できるか、料金を支払う人を明文化します。二つの組織が互いの行を読めず、最後の owner を削除できず、同時招待が最後の一席を二重に使わず、ダウングレード後も既存データとメンバーが保たれれば検証完了です。次にプランと権限で利用人数上限を決め、設計上の背景はSushi SaaS の構造で確認してください。