Sushi SaaS の構造
強制されるレイヤー、そのトレードオフ、二つ目のアーキテクチャを作らず機能を追加する方法を説明します。
このガイドは、最初のプロダクト機能をどこに置くか判断する、導入チーム向けの説明です。Sushi SaaS はすべての機能領域で一つの水平アーキテクチャを使い、テストで規約を強制します。
リクエストが通る経路
src/app/** ルートとページ — HTTP の入口と出口
↓
src/services/** 業務ルール、調整、不変条件
↓
src/models/** 型付き永続化;db() を呼べる唯一の層
↓
src/db/** スキーマ、マイグレーション、接続ルートは入力を解析して応答します。サービスは処理が許可されるかを判断し、副作用を調整します。モデルはデータベースの読み書きを一手に引き受けます。データベース制約は、並行するリクエストに対する最後の防御です。
ブラウザーにも別の境界があります。
Server Component → サービスを直接呼ぶ
Client Component → src/api/** → 共通 API クライアント → /api/**サーバーから自分自身への HTTP 呼び出しを避け、クライアント側のエラーを一つのレスポンス契約にそろえます。正確な規則はアーキテクチャとエラー契約を参照してください。
水平レイヤーを選んだ理由
課金、クレジット、ストレージ、予約、タスクは、いずれも認証、テナントの範囲、エラー処理、永続化を必要とします。同じレイヤーを使うことで責務の置き場所が予測可能になり、アーキテクチャテストが規約外の近道を検出できます。
一方で、一つの機能が複数のディレクトリに分かれます。大規模なプロダクトでは、機能ごとにまとめる垂直構造の方が追いやすい場合もあります。ただし二つの構造を混ぜると、次のクエリや業務ルールの置き場所が二つになり、どちらか一方に統一するより分かりにくくなります。
選択肢
- 既定構造を維持:強い規約と共通基盤を求める小規模チーム向けです。
- 垂直スライスへ全面移行:それがチーム標準なら、機能領域全体を移動し、
tests/unit/architecture.test.tsも更新します。既存規則の横にsrc/featuresという別方式の島を作らないでください。 - 短期プロトタイプ向けに簡略化:後で移行する費用を受け入れる場合だけです。ルートから DB へ直接書き込むなら、例外を増やすのではなく、テストも意図に合わせて変更します。
テナント設計もアーキテクチャ判断
すべてのユーザーに個人用の組織が作られます。そのため、一人で使う顧客もチームも、同じ組織単位の課金、クレジット、ファイル、上限を利用でき、「ユーザー所有」と「チーム所有」という二つの経路を持たずに済みます。
将来チーム機能を追加する可能性があるなら、この設計を維持します。常に一人で使う製品なら組織の仕組みを除く選択もできますが、認可、スキーマ、モデル、課金、ストレージ、テストを一緒に変える必要があり、画面上の切り替えだけでは済みません。組織とチームを読んで判断してください。
管理コンソールを分けた理由
apps/admin は独自のページ、管理 API、データアクセス、MFA の入口、運用担当者の権限を持ちます。スキーマと認証データを共有しながら、別のオリジンへデプロイできます。ビルドとデプロイが一つ増える代わりに、運用アクセスの境界が明確になります。
一つのデプロイにまとめたい場合は顧客アプリへ統合できます。ただし、サーバー側の管理者認可と監査は維持してください。ナビゲーションを隠すだけではセキュリティ対策になりません。管理コンソールの設定から確認します。
横方向 layer を通る完全な縦 journey
画像 reference は構造の価値を示します。Route が auth、service が fingerprint、deterministic 5-credit spend、durable dispatch、model が SQL、DB constraint が relation、replaceable provider adapter、worker が private output または ledger compensation を担います。Route、service、DB、component、Playwright test が別々の boundary を証明します。
最初の有料 domain を設計する前に、1 つの 5-credit task が SaaS 全体をテストする理由を読んでください。
最初の機能領域を追加する
src/typesとsrc/configで用語と上限を定義します。src/dbにスキーマと、段階的な展開に安全なマイグレーションを追加します。- 型付き CRUD を
src/modelsに置きます。 - 認可、冪等性、副作用を
src/servicesに置きます。 - ルートまたは Server Component を
src/appに追加し、クライアントからの呼び出しにはsrc/apiを使います。 - サービスの規則、ルートの認証ゲート、金銭・クレジット処理の再実行をテストします。
プラン名を直接比較せず、利用可能な機能を確認してください。契約が変わったら運用手順書も更新します。本ガイドはスターターのコミット 2a1a04a に基づきます。