核心原則:約束守護資料完整性,程式碼守護業務體驗
必須在資料庫層面加約束的場景
- 主鍵 — 唯一識別,防止重複實體。
- 唯一鍵 — 業務鍵 (如身分證字號、Email、手機號) 唯一性,防止臟資料導致查詢/刪除異常。
- 外鍵 — 維護參考完整性,防止孤兒資料。
- 非空 — 業務強制欄位。
- Check 約束 — 固定枚舉值 (如狀態碼 0/1/2)、範圍檢核 (年齡 > 0)、跨欄邏輯 (結束時間 > 開始時間)。
理由:資料庫是最後一道防線,任何繞過應用層的直連操作 (ETL、報表、DBA 手動修改) 都受保護。
建議放在應用層驗證的場景
- 格式檢核 — Email 格式、手機號規則、密碼複雜度。理由:規則常變、需國際化、正則複雜,資料庫
CHECK 難維護。
- 跨表/跨服務業務規則 — 「同一用戶同一天只能下單 3 次」、「庫存扣減前須檢查促銷資格」。理由:涉及多實體、事務邊界,屬應用層領域邏輯。
- 使用者友善錯誤訊息 — 資料庫拋出
ORA-00001 / 2627 無法直接給前端顯示,應用層捕獲後轉為「此手機號已註冊」。
雙重防禦最佳實踐
- 資料庫建立核心完整性約束 (PK, UK, FK, NOT NULL, 關鍵 CHECK)。
- 應用層 (Service/DTO/Validator) 實作完整業務驗證,回傳結構化錯誤碼與友善訊息。
- 資料庫異常捕獲轉換:
catch (DbUpdateException ex) when (ex.InnerException is SqlException sql && sql.Number == 2627) => throw new BizException("DuplicateKey", "資料已存在")。
反模式警示
- ❌ 完全依賴資料庫約束,前端無驗證 → 用戶體驗差、除錯難。
- ❌ 完全依賴應用層驗證,資料庫裸奔 → 一條髒 SQL 就能毀掉系統。
- ❌ 將所有格式檢核寫成資料庫
CHECK → 版本升級需 DBA 協助、測試成本高。
總結
「資料庫約束 = 安全網,應用驗證 = 導航儀」。兩者互補、分工明確,才能構建健壯且可維護的資料層。