"Invariant"
- Invariant
領域模型的約束規則落在哪一層時使用。不變式是在物件整個生命週期都必須為真的業務規則——狀態只能沿流程轉換、被同一條規則綁住的欄位必須一起換。
- 不變式的強制層次
業務約束落在文件層、型別層、執行層的差異與代價:違反時是靜默、編譯失敗還是當場拒絕。含不變式被撞時「需求違規 vs 約束錯形」的分辨、存在條件與輸入品質的分層邊界。
- Property-Based Testing(性質式測試)
逐例斷言列不完、或預期輸出難以逐例算出時,用來把驗證對象從個別例子換成對所有輸入都該成立的性質
- 單調狀態機與樂觀更新的回滾契約:前台不得顯示後端沒記錄的狀態
POS App 的品項處理狀態只能遞增——現實世界的動作不可逆,狀態機跟著不可逆。樂觀更新讓 UI 先行,但後端拒絕時必須回滾:因為這個狀態是其他防護規則的資料來源,前台多顯示一格進度,防護就會在錯誤的前提上放行。
- 「978ABC」被拒的理由寫著長度不對 — 驗證的兩層分工與順序陷阱
輸入驗證有兩層職責:建構期不變式守「這個物件能不能存在」、無狀態 validator 守「使用者輸入對不對」,混在一起會讓測試建不出 fixture、錯誤訊息歸錯類。順序陷阱:先標準化再檢查等於先銷毀證據再診斷——含字母的 ISBN 被削成三位數、錯誤訊息說長度不對。
- Exception 型別綁 ErrorCategory 的建構不變式 — 以及合法需求撞上不變式的時刻
把「錯誤代碼必須屬於對應分類」做成建構期不變式,錯誤分類錯亂會變成測試失敗而不是靜默混亂;同一批修復出現三種形態——換對值、換精確值、以及改繼承逃離約束。第三種是分類學本身的訊號:一個 domain 的錯誤天生橫跨技術分類時,分類軸跟階層軸不正交。
- 會員身分、計價、支付方式必須一起換 — 耦合欄位的原子切換
多個狀態欄位被同一條業務規則綁住時,分開的 setter 會製造不一致的中間態;把切換收成單一方法、一次狀態更新內同步全部欄位,並注意衍生值重算的順序。以 POS 結帳的會員登出重算為例,含不變式收進 model 的 canCheckout 設計。