昨天完成了 UI/UX 設計,今天要搭建技術架構。這不是選技術棧的炫技大會,而是要設計一個能撐起 MVP、又能優雅擴展到百萬用戶的架構。
很多人覺得 MVP 階段不用管架構,「能動就好」。但我見過太多專案因為初期架構債累積,導致:
正確的架構不是過度設計,而是剛好夠用的設計。
經過與 AI 深度討論,我選擇了這套現代化架構:
前端框架:Next.js 15 (App Router)
後端服務:Supabase (BaaS)
AI 服務:OpenAI + Vercel AI SDK
部署平台:Vercel
監控分析:Vercel Analytics + Sentry
Next.js 15 + App Router 的選擇邏輯:
Supabase 作為 BaaS 的價值:
關鍵決策:不自建後端
傳統架構:前端 + Node.js API + PostgreSQL + Redis + Auth
我的架構:Next.js + Supabase(包含所有後端功能)
省下的時間:至少 3 週
省下的成本:每月 $100+ 的伺服器費用
┌─────────────────────────────────────────┐
│          Presentation Layer             │
│  (React Components + Next.js Pages)     │
├─────────────────────────────────────────┤
│          Application Layer              │
│    (Use Cases + Business Logic)         │
├─────────────────────────────────────────┤
│           Domain Layer                  │
│    (Entities + Domain Services)         │
├─────────────────────────────────────────┤
│        Infrastructure Layer             │
│ (Supabase + OpenAI + External APIs)     │
└─────────────────────────────────────────┘
負責用戶介面和互動,但不包含業務邏輯。
目錄結構:
app/
├── (auth)/          # 認證相關頁面
│   ├── login/
│   └── register/
├── (dashboard)/     # 主要應用頁面
│   ├── page.tsx     # 首頁(記帳)
│   ├── timeline/    # 時間軸
│   ├── insights/    # 洞察報表
│   └── settings/    # 設定
├── components/      # 共用元件
│   ├── ui/         # 基礎 UI 元件
│   ├── features/   # 功能元件
│   └── layouts/    # 布局元件
└── hooks/          # Custom Hooks
設計原則:
協調領域層和基礎設施層,實現具體的使用案例。
目錄結構:
lib/
├── use-cases/       # 使用案例
│   ├── expenses/    # 支出相關
│   │   ├── quick-entry.ts
│   │   ├── categorize.ts
│   │   └── bulk-import.ts
│   ├── subscriptions/ # 訂閱相關
│   │   ├── identify.ts
│   │   ├── manage.ts
│   │   └── remind.ts
│   └── insights/     # 洞察相關
│       ├── generate-report.ts
│       └── analyze-pattern.ts
├── services/        # 應用服務
│   ├── auth.ts     # 認證服務
│   ├── notification.ts
│   └── export.ts
└── dto/            # 資料傳輸物件
核心職責:
包含核心業務邏輯,與技術無關。
目錄結構:
domain/
├── entities/        # 領域實體
│   ├── expense.ts
│   ├── subscription.ts
│   ├── budget.ts
│   └── category.ts
├── value-objects/   # 值物件
│   ├── money.ts
│   ├── period.ts
│   └── confidence.ts
├── aggregates/      # 聚合根
│   ├── expense-aggregate.ts
│   └── subscription-aggregate.ts
├── services/        # 領域服務
│   ├── categorization.ts
│   └── budget-guardian.ts
└── events/          # 領域事件
    ├── expense-events.ts
    └── subscription-events.ts
DDD 實踐要點:
所有外部系統的介面和實作。
目錄結構:
infrastructure/
├── supabase/        # Supabase 整合
│   ├── client.ts
│   ├── auth.ts
│   ├── database.ts
│   └── migrations/
├── ai/              # AI 服務
│   ├── openai.ts
│   ├── categorizer.ts
│   └── analyzer.ts
├── repositories/    # 資料存取
│   ├── expense.repo.ts
│   └── subscription.repo.ts
└── external/        # 第三方 API
    ├── telegram.ts
    └── bank-api.ts
用戶輸入「星巴克 120」
    ↓
[Presentation] Input Component
    ↓
[Application] QuickEntryUseCase
    ↓
[Domain] ExpenseAggregate.create()
    ↓
[Infrastructure] AI Categorizer
    ↓
[Domain] Category.assign()
    ↓
[Infrastructure] [ExpenseRepository.save](http://ExpenseRepository.save)()
    ↓
[Application] NotificationService
    ↓
[Presentation] UI Update
Next.js App Router 的 Server Components 改變了資料流:
// app/(dashboard)/page.tsx
// 這在伺服器端執行,不會發送到客戶端
async function DashboardPage() {
  // 直接在元件中 fetch 資料
  const expenses = await getRecentExpenses()
  const insights = await generateInsights(expenses)
  
  // 只傳送 HTML 到客戶端
  return (
    <Dashboard 
      expenses={expenses}
      insights={insights}
    />
  )
}
優點:
利用 Supabase Realtime 實現即時同步:
訂閱建立 → Supabase Channel
    ↓
資料變更 → PostgreSQL Trigger
    ↓
事件廣播 → WebSocket
    ↓
客戶端更新 → React State
應用場景:
1. Server State(伺服器狀態)
2. Client State(客戶端狀態)
3. URL State(路由狀態)
URL State(篩選、排序、頁數)
    ↓
Server State(資料查詢)
    ↓
Client State(UI 互動)
避免的反模式:
AI Gateway Layer
├── Rate Limiting
├── Fallback Logic
└── Response Cache
    ↓
AI Services
├── Categorization Service
├── Pattern Recognition
└── Insight Generation
    ↓
Model Layer
├── OpenAI GPT-4
├── Local Fallback Rules
└── User Feedback Loop
// 三層分類策略
class CategorizationStrategy {
  async categorize(expense: Expense): Promise<Category> {
    // Layer 1: 用戶歷史規則
    const userRule = await findUserRule(expense)
    if (userRule?.confidence > 0.9) return userRule.category
    
    // Layer 2: AI 預測
    const aiPrediction = await openAI.categorize(expense)
    if (aiPrediction.confidence > 0.7) return aiPrediction.category
    
    // Layer 3: 預設規則
    return applyDefaultRules(expense)
  }
}
Request → Cache Check → AI Call → Cache Store
              ↓              ↓
         Cache Hit      Token Count
              ↓              ↓
         Return Cache   Cost Control
策略:
Layer 1: Edge Security (Vercel)
├── DDoS Protection
├── Rate Limiting
└── Geographic Restrictions
Layer 2: Application Security (Next.js)
├── CSRF Protection
├── Content Security Policy
└── Input Validation
Layer 3: API Security (Supabase)
├── Row Level Security
├── JWT Validation
└── API Key Management
Layer 4: Data Security
├── Encryption at Rest
├── Encryption in Transit
└── PII Masking
Supabase 的 RLS 確保資料隔離:
-- 用戶只能看到自己的支出
CREATE POLICY "Users can only see own expenses" 
ON expenses FOR SELECT 
USING (auth.uid() = user_id);
-- 防止跨用戶資料洩漏
CREATE POLICY "Users can only insert own expenses" 
ON expenses FOR INSERT 
WITH CHECK (auth.uid() = user_id);
L1: Browser Cache
├── Static Assets (永久快取)
├── API Responses (SWR)
└── Service Worker
L2: CDN Cache (Vercel Edge)
├── HTML Pages
├── API Routes
└── Images
L3: Database Cache
├── PostgreSQL Query Cache
├── Materialized Views
└── Connection Pool
監控點布局:
User Action → Performance Mark
    ↓
API Call → Timing Measure
    ↓
Database Query → Trace
    ↓
Response → Core Web Vitals
目標指標:
MVP (Now)
├── Vercel Hobby
├── Supabase Free
└── OpenAI Pay-as-you-go
Growth (1K users)
├── Vercel Pro
├── Supabase Pro
└── OpenAI with Cache
Scale (10K users)
├── Vercel Enterprise
├── Supabase Team
├── OpenAI + Self-hosted Models
└── Redis Cache Layer
Enterprise (100K+ users)
├── Multi-region Deployment
├── Database Sharding
├── Custom AI Infrastructure
└── Dedicated Support
雖然 MVP 不需要,但架構已預留:
Local Development
├── Next.js Dev Server
├── Supabase Local (Docker)
├── TypeScript Watch
└── Hot Module Replacement
Testing Environment
├── Unit Tests (Vitest)
├── Integration Tests (Playwright)
├── E2E Tests
└── Visual Regression
CI/CD Pipeline
├── GitHub Actions
├── Vercel Preview
├── Automatic Deploy
└── Rollback Support
// 從資料庫到前端的類型安全
Database Schema (Supabase)
    ↓
TypeScript Types (Generated)
    ↓
Zod Schemas (Runtime Validation)
    ↓
React Components (Type-safe Props)
1. Application Monitoring (Sentry)
   - Error Tracking
   - Performance Monitoring
   - User Sessions
2. Infrastructure Monitoring (Vercel)
   - Function Execution
   - Edge Network
   - Build Performance
3. Business Monitoring (Custom)
   - User Behavior
   - Feature Usage
   - Conversion Funnel
4. AI Monitoring
   - Token Usage
   - Response Quality
   - Cost Tracking
狀態: 採用
決策: 使用單一 repository
原因:
風險: 未來可能需要拆分
緩解: 保持模組邊界清晰
狀態: 延遲
決策: 保持 Monolithic 架構
原因:
未來: 當 DAU > 10K 時重新評估
狀態: 採用
決策: 使用關聯式資料庫
原因:
補充: JSONB 欄位提供彈性
這些是我知道但故意延後的:
## Technical Debt Registry
| ID | Description | Impact | Effort | Priority |
|----|------------|--------|--------|----------|
| TD-001 | No message queue | Medium | High | P2 |
| TD-002 | Simple error handling | Low | Medium | P3 |
| TD-003 | No rate limiting | High | Low | P1 |
今天設計的架構不是終點,而是起點。好的架構應該:
最重要的是:架構服務於業務,而非相反。
Day 17 - 智慧記帳 Day 6:核心功能開發日
今天打好了地基,明天開始蓋房子!