--- name: design-an-interface description: "Backend Agent 使用此技能探索多種 API 介面設計方案。根據「Design It Twice」原則,產生多種截然不同的設計,比較後選擇最佳方案。觸發時機:API 設計階段(Stage 4),由 be-api-design 技能呼叫。" --- # /design-an-interface — 介面設計探索 Backend Agent 使用此技能探索 API 介面設計方案。 基於 "A Philosophy of Software Design" 的 "Design It Twice" 原則:第一個想法通常不是最好的。產生多種截然不同的設計,然後比較選擇。 ## 職責 1. 針對模組需求,產生 2-3 種截然不同的介面設計方案 2. 每種方案使用不同的設計約束 3. 比較方案的優劣 4. 協助選擇或合成最佳方案 ## 輸入 - 模組描述(來自 PRD 的功能性需求) - 使用者故事和操作場景 ## 輸出 - 多種介面設計方案(含介面簽名、使用範例、優劣分析) - 方案比較和建議 ## 流程 ``` 收集需求 ↓ 產生 2-3 種設計方案(平行子代理) ↓ 呈現各方案 ↓ 比較方案(介面簡潔性、通用性、實作效率、深度) ↓ 合成最佳方案或選擇最適方案 ``` ### 步驟說明 **1. 收集需求** 在設計之前,先了解: - [ ] 這個模組解決什麼問題? - [ ] 誰是呼叫者?(其他模組、外部使用者、測試) - [ ] 關鍵操作有哪些? - [ ] 有什麼限制?(效能、相容性、現有模式) - [ ] 什麼應該隱藏在內部?什麼應該暴露在外? 提問:「這個模組需要做什麼?誰會使用它?」 **2. 產生設計方案(平行子代理)** 同時產生 3+ 種截然不同的方案。每個方案必須遵循不同的約束: ``` 方案 A:最小化方法數 — 目標 1-3 個方法 方案 B:最大化彈性 — 支援多種使用情境 方案 C:最佳化最常見操作 方案 D(可選):參考特定範式或框架的設計 ``` 每個方案需包含: 1. 介面簽名(types / methods) 2. 使用範例(呼叫者如何使用) 3. 這個設計隱藏了什麼複雜度 4. 這個方案的取捨 **3. 呈現方案** 逐一展示每個方案,包含: - 介面簽名:types, methods, params - 使用範例:呼叫者如何實際使用 - 隱藏的複雜度:小介面隱藏大量實作(好)vs 大介面薄實作(差) 讓使用者充分吸收每個方案後再進行比較。 **4. 比較方案** 依以下維度比較: - **介面簡潔性**:方法少、參數簡單 → 更容易學習和正確使用 - **通用性 vs 專用性**:彈性 vs 專注,取捨在哪 - **實作效率**:介面形狀是否允許高效內部實作? - **深度**:小介面隱藏大量複雜度(深模組,好)vs 大介面薄實作(淺模組,差) - **正確使用的容易度 vs 誤用的容易度** 用文字討論取捨,不要只用表格。強調方案分歧最大的地方。 **5. 合成最佳方案** 最佳設計往往結合多個方案的洞見。詢問: - 「哪個方案最適合你的主要使用情境?」 - 「其他方案有沒有值得納入的元素?」 ## 評估標準 來自 "A Philosophy of Software Design": **介面簡潔性**:方法少、參數簡單 = 更容易學習和正確使用。 **通用性**:能否處理未來的使用情境而不需要修改。但要避免過度通用。 **實作效率**:介面形狀是否允許高效實作?還是迫使內部實作變得彆扭? **深度**:小介面隱藏大量複雜度 = 深模組(好)。大介面薄實作 = 淺模組(避免)。 ``` 深模組(好): ┌─────────────────────┐ │ Small Interface │ ← 方法少,參數簡單 ├─────────────────────┤ │ │ │ │ │ Deep Implementation│ ← 複雜邏輯隱藏在內部 │ │ │ │ └─────────────────────┘ 淺模組(避免): ┌─────────────────────────────────┐ │ Large Interface │ ← 方法多,參數複雜 ├─────────────────────────────────┤ │ Thin Implementation │ ← 只是穿隧 └─────────────────────────────────┘ ``` ## 反模式 - 不要讓子代理產生相似的設計 — 強制截然不同 - 不要跳過比較 — 價值在於對比 - 不要在此階段實作 — 這純粹是介面設計 - 不要基於實作工作量評價方案 — 只看介面品質 ## Golang 介面設計範例 ```go // 方案 A: 最小化方法數 type UserRepository interface { GetByID(ctx context.Context, id string) (*domain.User, error) Save(ctx context.Context, user *domain.User) error } // 優點:簡潔,容易實作 mock // 缺點:Save 同時處理 Create 和 Update,取決於是否已存在 // 方案 B: 分離讀寫 type UserReader interface { GetByID(ctx context.Context, id string) (*domain.User, error) List(ctx context.Context, page, limit int) ([]*domain.User, error) } type UserWriter interface { Create(ctx context.Context, user *domain.User) error Update(ctx context.Context, user *domain.User) error Delete(ctx context.Context, id string) error } // 優點:CQRS 友好,職責分離 // 缺點:方法數較多,但每個方法語意更清楚 // 方案 C: 最佳化常見操作 type UserService interface { Register(ctx context.Context, email, password, name string) (*domain.User, error) Authenticate(ctx context.Context, email, password string) (*domain.User, error) GetProfile(ctx context.Context, id string) (*domain.User, error) } // 優點:直接對應業務操作,使用最直覺 // 缺點:每個新操作都要加方法,彈性較低 ``` ## 與 be-api-design 的整合 此技能由 `be-api-design` 在步驟 3 自動呼叫。API 設計流程: ``` be-api-design 步驟 1: 讀取 PRD be-api-design 步驟 2: 識別資源與操作 → design-an-interface: 探索 2-3 種 API 設計方案 be-api-design 步驟 4: 選定方案,定義 OpenAPI 規格 ``` ## 相依技能 - **前置**: `write-a-prd` (PRD 完成) - **後續**: `be-api-design` (API 規格定義)