189 lines
6.3 KiB
Markdown
189 lines
6.3 KiB
Markdown
|
|
---
|
|||
|
|
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 規格定義)
|