システムアーキテクチャ
このページでは、FeedbackPulse SaaS の内部構造を詳しく解説します。アーキテクチャを理解することで、問題のデバッグ、プラットフォームの拡張、適切なデプロイ判断がしやすくなります。
テックスタック
| レイヤー | 技術 | 用途 |
|---|---|---|
| バックエンド | Laravel 12(PHP 8.4+) | アプリケーションフレームワーク、ルーティング、ORM、キュー |
| データベース | MySQL 8.0+ / MariaDB 10.6+ | 永続的データストレージ |
| フロントエンド | Blade + Alpine.js + Tailwind CSS | リアクティブコンポーネントを持つサーバーレンダリング UI |
| 決済 | Stripe PHP SDK + PayPal REST API | サブスクリプション請求 |
| AI | OpenAI GPT API | センチメント分析、自動タグ付け、返信提案 |
| 認証 | Laravel Socialite | Google & GitHub OAuth |
| メール | Laravel Mail(SMTP) | トランザクションメール、ダイジェスト、レポート |
| リアルタイム | Server-Sent Events(SSE) | ライブ投稿ストリーミング |
| タスクスケジューリング | Laravel Scheduler(cron) | ダイジェスト、トライアル期限、データ保持 |
マルチテナンシーモデル
FeedbackPulse はシングルデータベース・共有スキーマのマルチテナンシーモデルを採用しています。つまり:
- 1 つのデータベースがすべてのテナントを扱います
- テナントスコープのすべてのテーブルには
tenant_id列があります - グローバルスコープ(
TenantScope)がすべてのクエリを現在のテナントに自動的にフィルタリングします - トレイト(
BelongsToTenant)が作成時にtenant_idを自動入力し、スコープを適用します
テナント解決の仕組み
リクエストが届くと、ResolveTenant ミドルウェアが以下の優先順位で現在のテナントを特定します:
- サブドメイン —
acme.yourdomain.com→ サブドメイン「acme」のテナントを検索 - カスタムドメイン —
feedback.acmecorp.com→tenant_domainsテーブルで検証済みドメインを検索 - 認証済みユーザー — ログイン中のユーザーの
tenant_idにフォールバック
パブリックページ(/wall/acme-corp など)の場合、テナントはコントローラー内で URL スラッグから直接解決されます(ミドルウェアをバイパス)。
データ分離
+------------------------------------------+
| データベース |
| |
| products (tenant_id = 1) -> Acme データ |
| products (tenant_id = 2) -> TechCorp |
| products (tenant_id = 3) -> E-Commerce |
| |
| TenantScope により Acme は |
| tenant_id = 1 の行のみ参照可能 |
+------------------------------------------+
セキュリティ: パブリック向けコントローラーは
withoutGlobalScopes()を使用してテナントスコープをバイパスし、手動でテナント ID によるフィルタリングを行います。これは意図的な設計です — パブリックページは認証済みセッションなしにデータを表示する必要があるためです。