
先講結論
Sequence Diagram 是設計文件裡 CP 值最高的一種圖。它能清楚回答:「當使用者做 X 的時候,系統裡面到底發生了什麼?」
什麼時候畫:
- 涉及 2 個以上服務的 API 呼叫流程
- 跨前後端的關鍵業務流程(登入、付款、訂單)
- 非同步訊息流(Kafka / RabbitMQ)的訊息傳遞順序
- 需要讓 QA 理解測試場景的例外流程
什麼時候不用畫:
- 只有一個服務、單純的 CRUD(直接看 API Spec 就夠了)
- 流程邏輯很簡單、三行文字就能說清楚的
Mermaid 語法速查
Mermaid 是目前最推薦的 Sequence Diagram 工具,可以直接渲染在 GitHub、Notion、VS Code、GitLab。
基本宣告
sequenceDiagram
actor User as 使用者 ← 人形圖示
participant Browser as 瀏覽器 ← 方框(系統/服務)
participant API as API Server
participant DB as 資料庫
訊息類型
A->>B: 同步請求(實線箭頭)
B-->>A: 同步回應(虛線箭頭)
A-)B: 非同步訊息,fire and forget(實線開頭箭頭)
A--xB: 跨越 X,訊息被拒絕/失敗(少用)
生命線(Lifeline)
activate A ← 啟動生命線(顯示 A 正在執行)
deactivate A ← 結束生命線
流程控制
alt 條件一
A->>B: 訊息
else 條件二
A->>C: 另一個訊息
end
opt 選擇性操作(只有 if,沒有 else)
A->>B: 訊息
end
loop 每 5 秒輪詢
A->>B: polling
B-->>A: 回應
end
備註
Note right of A: 右側備註
Note left of B: 左側備註
Note over A,B: 跨越 A 和 B 的備註
範本一:使用者登入
sequenceDiagram actor User as 使用者 participant Browser as 瀏覽器 participant API as API Server participant DB as 資料庫 participant Redis as Redis User->>Browser: 輸入 Email + 密碼,點擊登入 Browser->>API: POST /auth/login { email, password } activate API API->>DB: SELECT * FROM users WHERE email = ? activate DB DB-->>API: 回傳使用者資料 deactivate DB alt 帳號不存在 或 密碼錯誤 API->>DB: UPDATE users SET login_attempts = login_attempts + 1 API-->>Browser: 401 { code: "INVALID_CREDENTIALS" } else 連續失敗 5 次 API->>DB: UPDATE users SET locked_until = NOW() + 30min API-->>Browser: 429 { code: "ACCOUNT_LOCKED", retry_after: 1800 } else 帳號未驗證 API-->>Browser: 403 { code: "EMAIL_NOT_VERIFIED" } else 登入成功 API->>Redis: SET session:{token} userId, EX 1800 API-->>Browser: 200 { token, expires_in: 1800, user } deactivate API Browser->>User: 導向首頁 end
範本二:電商下訂單(跨服務)
sequenceDiagram actor User as 使用者 participant Web as Web App participant OrderSvc as Order Service participant InventorySvc as Inventory Service participant PaymentSvc as Payment Service participant NotifySvc as Notification Service participant OrderDB as Order DB User->>Web: 點擊「確認下單」 Web->>OrderSvc: POST /orders { items, userId } activate OrderSvc OrderSvc->>InventorySvc: POST /inventory/check { items } activate InventorySvc InventorySvc-->>OrderSvc: { available: true } deactivate InventorySvc alt 庫存不足 OrderSvc-->>Web: 400 { code: "INSUFFICIENT_STOCK", details: { product_ids: [...] } } Web->>User: 顯示庫存不足商品 else 庫存充足 OrderSvc->>OrderDB: INSERT orders (status: pending) OrderSvc->>InventorySvc: POST /inventory/reserve { items, orderId } OrderSvc->>PaymentSvc: POST /payments { orderId, amount } activate PaymentSvc PaymentSvc-->>OrderSvc: { paymentId, status: success } deactivate PaymentSvc alt 付款失敗 OrderSvc->>InventorySvc: POST /inventory/release { orderId } OrderSvc->>OrderDB: UPDATE orders SET status = failed OrderSvc-->>Web: 402 { code: "PAYMENT_FAILED" } else 付款成功 OrderSvc->>OrderDB: UPDATE orders SET status = confirmed OrderSvc-)NotifySvc: POST /notify { userId, orderId, type: "order_confirmed" } Note right of NotifySvc: 非同步,不等回應 OrderSvc-->>Web: 201 { orderId, status: confirmed } deactivate OrderSvc Web->>User: 顯示訂單確認頁 end end
範本三:API Gateway + 微服務認證
sequenceDiagram actor Client as 客戶端 participant Gateway as API Gateway participant AuthSvc as Auth Service participant UserSvc as User Service participant Cache as Redis Cache Client->>Gateway: GET /users/profile (Authorization: Bearer token) activate Gateway Gateway->>Cache: GET token:{token} activate Cache Cache-->>Gateway: 快取命中,回傳 userId deactivate Cache alt 快取未命中 Gateway->>AuthSvc: POST /auth/verify { token } activate AuthSvc AuthSvc-->>Gateway: { valid: true, userId: 123, expires_at: ... } deactivate AuthSvc Gateway->>Cache: SET token:{token} userId EX 300 end alt Token 無效 Gateway-->>Client: 401 Unauthorized else Token 有效 Gateway->>UserSvc: GET /users/123 activate UserSvc UserSvc-->>Gateway: { id: 123, name: "...", email: "..." } deactivate UserSvc Gateway-->>Client: 200 { user profile } deactivate Gateway end
範本四:Webhook 接收與非同步處理
sequenceDiagram participant Stripe as Stripe(第三方) participant API as API Server participant Queue as Message Queue participant Worker as Background Worker participant DB as 資料庫 participant NotifySvc as Notification Service Stripe->>API: POST /webhooks/stripe { event: "payment_intent.succeeded", ... } activate API Note over API: 驗證 HMAC 簽名 alt 簽名驗證失敗 API-->>Stripe: 400 Bad Request else 簽名驗證成功 API->>Queue: PUBLISH payment.completed { orderId, amount } API-->>Stripe: 200 OK deactivate API Note over API,Stripe: 立即回 200,不讓 Stripe 逾時重試 end Queue->>Worker: CONSUME payment.completed activate Worker Worker->>DB: UPDATE orders SET status = paid, paid_at = NOW() Worker->>DB: INSERT payments { orderId, amount, stripePaymentId } Worker->>NotifySvc: POST /notify { userId, type: "payment_confirmed" } Worker-->>Queue: ACK deactivate Worker
範本五:Token 刷新流程
sequenceDiagram actor User as 使用者 participant App as 前端 App participant API as API Server participant DB as 資料庫 User->>App: 操作任意功能 App->>API: GET /protected-resource (Access Token 過期) API-->>App: 401 { code: "TOKEN_EXPIRED" } App->>API: POST /auth/refresh { refresh_token } activate API API->>DB: SELECT * FROM refresh_tokens WHERE token = ? AND expires_at > NOW() activate DB DB-->>API: 回傳 Token 記錄 deactivate DB alt Refresh Token 無效或已過期 API-->>App: 401 { code: "REFRESH_TOKEN_EXPIRED" } App->>User: 導向登入頁 else Refresh Token 有效 API->>DB: 撤銷舊 Refresh Token API->>DB: 新增新 Refresh Token API-->>App: 200 { access_token, refresh_token, expires_in } deactivate API App->>API: 重試原始請求(帶新 Access Token) API-->>App: 200 { 原始資源 } App->>User: 正常顯示 end
常見模式備忘
非同步 vs 同步
sequenceDiagram participant A participant B 非同步(fire and forget,不等回應) A-)B: 非同步訊息 %% 輪詢 loop 每 5 秒 A->>B: 狀態查詢 B-->>A: 回應 end
選擇性操作
sequenceDiagram participant A participant B opt 使用者有開啟通知 A->>B: 發送推播 B-->>A: 推播成功 end
畫圖原則
一張圖只說一件事。不要把所有流程塞進一張圖,分開畫,用文字說明流程間的關係。
例外流程要畫,但要分開。主要流程和例外流程可以合在一張圖(用 alt),但如果例外流程複雜,可以拆出單獨的圖說明。
命名要用業務語言。服務名稱叫 Order Service 而不是 src/services/orderService.ts;事件叫「付款成功」而不是 payment_intent.succeeded。
標注非同步邊界。什麼是同步等待、什麼是非同步 fire and forget,要在圖上或備註裡說清楚。
