Sushi SaaS の構造
スターターが強制するルート、サービス、モデル、データベースの各レイヤーと、独立した管理アプリを解説します。
Sushi SaaS は「データは明確なレイヤーを下方向に流れる」という一つの規則で構成されています。単なるフォルダーの推奨ではなく、境界を越える import はアーキテクチャテストで拒否されます。
アプリケーション内の流れ
src/app/** ルートとページ
↓
src/services/** 業務ルールとオーケストレーション
↓
src/models/** 型付き永続化
↓
src/db/** スキーマ、マイグレーション、接続ルートは HTTP を変換します。サービスは冪等性、認可、台帳ルール、副作用などの不変条件を持ちます。db() を呼べるアプリケーション層はモデルだけで、データベース制約が並行処理に対する最後の境界です。
ブラウザー側にも明確な分離があります。Server Component はサービスを直接呼び、Client Component は src/api/** のラッパーと共通 API client を使います。サーバーから自分自身への HTTP 呼び出しと、コンポーネントごとの独自なレスポンス処理を避けるためです。
実行可能な規則とエラー形式はアーキテクチャとエラー契約を参照してください。
全ドメインが同じレイヤーを使う
組織、課金、クレジット、ストレージ、予約、タスクは、別々の小さなアプリではありません。各ドメインが同じ水平レイヤーを横断します。たとえばクレジット消費はルートから入り、サービスで認可と冪等性を保証し、モデルで保存し、台帳制約で保護します。
この一貫性により、新しい責務を置く場所が一つに決まります。テナント境界については組織とチームを参照してください。
管理コンソールは独立アプリ
apps/admin は独自のページ、管理 API、データアクセス、MFA ゲート、読み書きロールを持つ別の Next.js アプリです。スキーマと中核認証は共有しますが、別オリジンにデプロイでき、顧客アプリから運用ルートを分離します。
デプロイ前に管理コンソールの設定とアクセスを確認してください。
拡張時に守る境界
- 業務不変条件はルートではなくサービスに置く。
- DB アクセスはコンポーネントやサービスではなくモデルに置く。
- プラン名の比較ではなく capability を確認する。
- 管理専用のクエリと endpoint は
apps/adminに置く。 - 実装契約が変わったらバージョン管理された runbook も更新する。
このガイドは starter commit 7580470 に照らして確認済みです。