❯❯ flow.md 實戰:六條不變量,和台灣婚宴的「10+1」領域知識
📍 流水線位置|入口 → 【flow】 → 型別 → mock → 測試 → UI → vibe → 上線(實戰)

在整個婚禮系統中,座位分配(Seating)無疑屬於業務規則複雜度極高的高難度模組。它的難點在於狀態繁複、邊界條件多,且充滿動態變更的特性。從每桌的標準席位設定、兒童椅的彈性加設,到手動排座的調整,以及任何異動引發的座位狀態連動,無一不考驗著系統的穩定度。
這種「規則密度極高、邊界條件極多」的複雜場景,正好是用來驗證 Flow 層(spec/e2e-flows/)能否真正發揮「業務護欄」功用的絕佳試金石。
這一篇,我們就直接打開實體檔案 spec/e2e-flows/04-seating.flow.md,來一場深度解剖。
檔案的開頭,開宗明義地劃清了這份 Flow 的責任邊界:它一口氣涵蓋了多達六個 .feature 規格檔案,從桌次的新增、更新與移除,到場地佈局設定,再到賓客的入座與取消入座,都需要精確對應至前端的場地平面圖視圖(Layout View)。
將「規格與測試流程」的對照關係明確定義於檔頭,能確保未來一旦出現業務邏輯異常時,團隊能在第一時間進行精確的責任溯源,不再需要盲目猜測。

在04-seating.flow.md 中,我們從原始規格中精確萃取出了六條關鍵的業務不變量(Business Invariants),節錄如下(完整條文見上方截圖):
1. 桌次增刪修:
管理員能在平面圖新增、更新或移除桌次(包含名稱、座位數與座標)。
2. 破壞性操作防禦:
桌次上若仍有賓客入座,系統嚴禁直接移除該桌。
3. 場地佈局設定:
管理員能設定場地佈局(如舞台位置與大小)。
4. 座位指派與展開:
管理員能將賓客(含同行者與兒童椅嬰兒)安排至桌次或取消入座;一個賓客組會依人數展開為多個席位。
5. 容量計算規則:
容量嚴格以「人頭」計算,正常席不可超過 capacity(兒童椅為額外加位,不佔正常席);正常席已滿或賓客已有座位時,不可重複安排。
6. 資訊可讀性:
桌次識別欄位(tableName)與正常席容量(capacity)必須能被使用者清晰讀取。
其中在這六條規範中,第 2 條與第 5 條更是最具代表性的關鍵邊界約束:

第 5 條:「容量以人頭計:正常席人頭不可超過 capacity,兒童椅額外加位、不佔正常席」:
此規則直接反映了台灣婚宴的實務領域知識(Domain Know-how)。傳統婚宴桌席多以 10 人為標準容量,若賓客攜帶嬰幼兒,實務上會採額外加設兒童椅的「10+1」模式。
如果沒有把這項邊界條件明確寫進規格,通用 LLM 極容易寫出 guests.length <= capacity 這種過度簡化的判斷邏輯,把不佔正常席的兒童椅算進標準容量裡,導致明明沒滿座的桌次被系統誤判為爆滿。唯有將這類隱性的領域知識鍛造成明確的 Specification,入座流程的容量計算才能獲得真正的工程約束。
※註:這條容量約束後來在「調降座位數」的邊界邊緣曾爆出邏輯漏洞,後續我們已透過 Issue #90 補齊 HTTP 409 衝突守門機制,這也再次印證了護欄演進的重要性。
領域邏輯從來不會憑空變成防禦程式碼。Specification 最核心的工程價值,就是把這些沉澱在專家腦中的「隱性領域知識」,轉譯成系統可嚴格執行的硬性約束。
以「成功新增桌次」的場景為例,Flow 文件中的驗證流程始終保持著高階抽象。它的操作步驟大致為:「進入座位頁面 ➔ 觸發新增桌次 ➔ 填寫名稱『主桌』、容量 10 與座標位置 ➔ 提交」。全程完全不綁定具體的 DOM 結構或介面細節,例如:按鈕顏色或是 Modal 視窗的開啟方式。
它的斷言策略精準鎖定在兩個層級:
API 層(API Spy):
嚴格斷言發往 POST /api/v1/weddings/wedding-001/tables 的 HTTP 請求 Payload 必須精確包含名稱、容量與座標參數。
UI 語意層:
僅斷言 DOM 樹中成功渲染出包含名稱「主桌」且容量標示為 10 的實體節點。
這種設計帶來了極為顯著的工程效益。在後續的迭代過程中,座位配置頁面經歷了多次 UI 重構,包含引入場地底圖繪製、升級佈局工具,以及補充行動端觸控操作支援。儘管介面互動層進行了翻天覆地的重構,但基於業務不變量撰寫的主規格測試套件全程保持通過。原因就在於「不再凍結」段落早就為我們劃清了安全邊界:「位置設定方式(拖放畫布 / 數字輸入)」屬於允許自由重構的視覺與互動範疇。

事實上,flow.md 絕非寫完就晾在那裡的靜態文件,而是貫穿整條自動化流水線的核心結構化原料:
Verification 中定義的 API 路徑與 Payload 規範:將作為下一步驟(Day 10)自動生成強型別合約與 Client 端的權威基準。Business Invariants 段落:則會順流而下,成為 Day 14 中凍結層 E2E 測試套件的自動化斷言依據。同一份業務邏輯真理(SSoT),會沿著這條自動化管線,在不同階段演化出相對應的工程實作。但這一切的前提是:這份「真理」本身必須是對的。
搞定 Flow 層之後,下一篇要碰一個尷尬的問題:flow.md 定義的規格範圍,如果哪天發現它自己沒用、根本不該存在,那這份當初說好的『凍結』,到底還算不算數?
📎 本篇證據|
spec/e2e-flows/04-seating.flow.md(包含六條 Business Invariants 與各場景「不再凍結」段落之實體文字)