# ADR-021: Playbook 更新驗證策略 | 屬性 | 值 | |------|-----| | **狀態** | Accepted | | **建立日期** | 2026-03-26 | | **決策者** | 首席架構師 | | **關聯** | Phase 8 P1 修復, leWOOOgo 積木化原則 | ## 背景 Router 層 (`playbooks.py`) 使用 `setattr()` 動態更新 Playbook 欄位,繞過 Pydantic 驗證: ```python # ❌ 問題代碼 (Router 層) update_data = request.model_dump(exclude_unset=True) for field, value in update_data.items(): setattr(playbook, field, value) # 無驗證! ``` 問題: 1. **資料完整性** - 非法狀態值可直接寫入 2. **違反分層** - Router 層執行業務邏輯 3. **安全風險** - 可修改 `playbook_id`, `success_count` 等欄位 ## 決策 將更新邏輯移至 Service 層,實作 `update_with_validation()` 方法。 ### 驗證規則 | 規則 | 說明 | |------|------| | **禁止修改欄位** | `playbook_id`, `created_at`, `success_count`, `failure_count`, `last_used_at` | | **狀態轉換** | DRAFT → APPROVED (允許), APPROVED → DEPRECATED (允許), 反向 (禁止) | | **DEPRECATED 狀態** | 不可修改任何欄位 | ### 實作 ```python # ✅ Service 層 (playbook_service.py) async def update_with_validation( self, playbook_id: str, update_data: dict, ) -> Playbook | None: # 1. 過濾禁止修改的欄位 # 2. 驗證狀態轉換 # 3. 應用更新 # 4. 儲存 ``` ```python # ✅ Router 層 (playbooks.py) updated = await service.update_with_validation( playbook_id=playbook_id, update_data=request.model_dump(exclude_unset=True), ) ``` ## 影響 | 檔案 | 變更 | |------|------| | `src/services/playbook_service.py` | 新增 `update_with_validation()` | | `src/api/v1/playbooks.py` | 改用 Service 方法 | ## 替代方案 | 方案 | 優點 | 缺點 | 決定 | |------|------|------|------| | 維持現狀 (setattr) | 無需改動 | 繞過驗證 | ❌ | | Pydantic validator | 自動驗證 | 無法檢查狀態轉換 | ❌ | | **Service 層驗證** | 完整控制 | 需新增方法 | ✅ | ## 相關 - ADR-003: leWOOOgo 積木化架構 - Skill 02: leWOOOgo Backend Core