メインコンテンツまでスキップ

システムアーキテクチャ

This page is a deep-dive into how FeedbackPulse SaaS is built. Understanding the architecture helps you debug issues, extend the platform, and make informed deployment decisions.


技術スタック

LayerTechnologyPurpose
BackendLaravel 12 (PHP 8.2+)Application framework, routing, ORM, queue
データベースMySQL 8.0+ / MariaDB 10.6+Persistent data storage
FrontendBlade + Alpine.js + Tailwind CSSServer-rendered UI with reactive components
PaymentsStripe PHP SDK + PayPal REST APISubscription billing
AIOpenAI GPT APISentiment analysis, auto-tagging, reply suggestions
AuthLaravel SocialiteGoogle & GitHub OAuth
EmailLaravel Mail (SMTP)Transactional emails, digests, reports
Real-TimeServer-Sent Events (SSE)Live submission streaming
Task SchedulingLaravel Scheduler (cron)Digests, trial expiry, データ保持

マルチテナンシーモデル

FeedbackPulse uses a single-database, shared-schema multi-tenancy model. This means:

  • One database serves all tenants
  • Every tenant-scoped table has a tenant_id column
  • A global scope (TenantScope) automatically filters all queries to the current tenant
  • A trait (BelongsToTenant) auto-fills tenant_id on creation and applies the scope

テナント解決の仕組み

When a request comes in, the ResolveTenant middleware determines the current tenant using this priority:

  1. Subdomainacme.yourdomain.com → looks up tenant with subdomain "acme"
  2. Custom domainfeedback.acmecorp.com → looks up verified domain in tenant_domains table
  3. Authenticated user — falls back to the logged-in user's tenant_id

For public pages (like /wall/acme-corp), the tenant is resolved from the URL slug directly in the controller (bypassing middleware).

データ分離

+------------------------------------------+
| Database |
| |
| products (tenant_id = 1) -> Acme data |
| products (tenant_id = 2) -> TechCorp |
| products (tenant_id = 3) -> E-Commerce |
| |
| TenantScope ensures Acme ONLY sees |
| rows where tenant_id = 1 |
+------------------------------------------+

セキュリティ: Public-facing controllers use withoutGlobalScopes() to bypass the tenant scope, then manually filter by tenant ID. This is intentional — public pages need to show data without an authenticated session.


ディレクトリ構造

feedbackpulse-saas/
+-- app/
| +-- Console/Commands/ # 7 artisan commands (digests, retention, alerts)
| +-- Http/
| | +-- Controllers/
| | | +-- Admin/ # Superadmin panel controllers
| | | +-- Auth/ # Login, register, 2FA, OAuth, impersonation
| | | +-- Customer/ # Customer portal
| | | +-- Public/ # Public pages (wall, form, roadmap, changelog, hub)
| | | +-- Tenant/ # Tenant dashboard controllers
| | | +-- Webhooks/ # Stripe & PayPal webhook handlers
| | +-- Middleware/ # 15 custom middleware
| +-- Mail/ # 7 mailable classes
| +-- Models/ # 28 Eloquent models
| +-- Scopes/ # TenantScope (global query scope)
| +-- Services/ # Business logic (AI, payments, webhooks, etc.)
| +-- Traits/ # BelongsToTenant trait
| +-- Providers/ # Service providers
+-- config/ # 11 Laravel config files
+-- database/
| +-- migrations/ # 35+ migration files
| +-- seeders/ # Demo data seeders
+-- public/ # Web root (index.php, assets, storage symlink)
+-- resources/views/ # 86 Blade templates
| +-- admin/ # Superadmin views
| +-- auth/ # Auth views (login, register, 2FA)
| +-- tenant/ # Tenant dashboard views
| +-- public/ # Public page views
| +-- emails/ # Email templates
| +-- layouts/ # Layout templates (admin, tenant, guest, install)
| +-- partials/ # Shared components (nav, meta, command palette)
| +-- install/ # Web installer views
| +-- legal/ # Privacy, terms, cookies pages
| +-- errors/ # Error pages (403, 404, 419, 429, 500)
| +-- landing/ # Landing page partials
+-- routes/
| +-- web.php # 416 lines of web routes
| +-- api.php # API v2 routes
+-- storage/ # Uploads, cache, sessions, logs
+-- bootstrap/ # Framework bootstrap files

ユーザーロール

FeedbackPulse has four user roles, stored in the users.role column:

RoleAccessLogin URL
superadminFull platform control (/admin/*)/login
tenant_adminFull tenant control (/dashboard, /settings/*)/login
tenant_staffLimited tenant access (no billing, no delete)/login
customerCustomer portal only (/customer/dashboard)/customer/login

ロール階層

superadmin
+-- Can impersonate any tenant_admin
+-- tenant_admin
+-- Can invite tenant_staff
+-- Can manage billing, settings, team
+-- tenant_staff
+-- Can manage submissions, campaigns
+-- customer
+-- Can view their own feedback

リクエストライフサイクル

Here's what happens when a request hits FeedbackPulse:

Browser Request
|
v
public/index.php
|
v
Laravel Kernel (middleware stack)
|
+-- EnsureInstalled -> Redirect to /install if not set up
+-- SecurityHeaders -> Add HSTS, CSP, X-Frame-Options
+-- VerifyCsrfToken -> Check CSRF token (except webhooks)
+-- ResolveTenant -> Determine current tenant
+-- Authenticate -> Check if user is logged in
+-- EnsureTenantAccess -> Verify user belongs to tenant
+-- EnsureTwoFactorVerified -> Check 2FA if enabled
+-- CheckPlanLimit -> Enforce plan feature limits
|
v
Controller (processes request)
|
v
Blade View (renders HTML)
|
v
Response -> Browser

データベースアーキテクチャ

コアテーブル

TableRows PerPurpose
tenantsPlatformMulti-tenant accounts
usersPlatformAll user accounts (all roles)
plansPlatformSubscription plans
platform_settingsPlatformGlobal key-value config
productsTenantFeedback products
feedback_campaignsTenantFeedback form configurations
feedback_submissionsTenantIndividual feedback entries
feedback_tagsTenantTags (many-to-many with submissions)
roadmap_itemsTenantロードマップ kanban items
roadmap_votesTenantAnonymous votes on roadmap items
feature_requestsTenantCommunity feature suggestions
changelog_entriesTenantProduct release notes
team_membersTenantTeam member records
team_invitationsTenantPending invitations
api_keysTenantAPI access keys
audit_logsPlatformAction audit trail
notificationsPlatformIn-app notifications
payment_eventsPlatformStripe/PayPal webhook events
webhook_logsTenantOutbound webhook delivery logs
data_deletion_requestsTenantGDPR deletion requests
landing_pagesPlatformLanding page builder data
tenant_domainsTenantCustom domain mappings
referral_codesTenantReferral codes
referral_conversionsPlatformReferral conversion tracking
cron_logsPlatformScheduled task execution logs

主要インデックス

All tenant-scoped tables are indexed on (tenant_id, created_at) for optimal query performance. The feedback_submissions table has additional indexes on status, product_id, campaign_id, and sentiment_label.


セキュリティアーキテクチャ

LayerProtection
TransportHTTPS enforced, HSTS headers
AuthenticationBcrypt (12 rounds), optional 2FA (TOTP)
AuthorizationRole-based middleware + policy classes
CSRFLaravel CSRF tokens on all forms
XSSBlade auto-escaping ({{ }})
SQL InjectionEloquent parameterized queries
Rate LimitingPer-route throttling (5-120 req/min)
API セキュリティSHA256-hashed APIキー, per-tenant rate limits
Webhook セキュリティHMAC signature verification (Stripe), SSRF protection
DataSensitive settings encrypted at rest
SessionsEncrypted, HTTP-only, secure cookies
HeadersCSP, X-Frame-Options, X-Content-Type-Options

次のステップ