我的任務
我引導協調器建立新功能(非修改既有功能),並事先進行復用、配置和相容性分析。與重構不同,這裡沒有既有的程式碼需要保留:設計從使用者的需求開始。
何時使用此技能(而非 refactor-protocol):
- 使用者要求一個不存在的端點、服務、作業、處理常式、事件或配置
- 任務是純粹的新增,而非修改現有行為
- 結果是向儲存庫新增檔案,而非改變既有檔案的簽章
何時不使用此技能(請改用 refactor-protocol):
- 修改既有服務的簽章
- 改變已存在的端點行為
- 重新命名、提取或移動程式碼
階段 0 — 結構驗證(永遠第一步)
- 載入「project-structure」技能並執行完整驗證
- 偵測主機類型:
FUNCTIONS|WEBAPP|NUGET_LIBRARY - 根據結果採取行動:
| 狀態 | 動作 |
|---|---|
HARD_BLOCK | 停止。通知使用者。 |
NUGET_LIBRARY | 建立 HTTP 端點是預料之外的情況。詢問:「您是想在函式庫中新增一個公開操作,還是確實有個我未偵測到的主機?」 |
PREGUNTA | 在繼續之前先解決問題 |
OK / WARNING | 繼續進行階段 1 |
階段 1 — 需求收集
協調器應從使用者的描述($ARGUMENTS)中推斷出任何可能的資訊,並且只詢問不清楚的部分。不要問描述中已經有答案的問題。
1.1 名稱、目的與功能類型
- HTTP 端點 (Function/Controller):
[VERB] /route — 描述 - 領域服務:服務名稱與職責
- 作業/事件處理常式:觸發器 + 動作
- 配置選項:選項名稱及其目的
1.2 唯讀 vs 變更分類
判斷該功能是變更狀態還是唯讀。這會影響:
- Response DTO 的放置位置
- 是否需要交易 / 冪等性
- 需要哪些測試
| 類型 | 特徵 | 影響 |
|---|---|---|
| READ-ONLY | GET,回傳既有數據,不寫入 | 暫時性的 DTO,最少測試 |
| MUTATION | POST/PUT/DELETE,寫入或改變狀態 | 需要成功與錯誤測試 |
1.3 收集預期的輸入/輸出
- Request:body 或 query 參數的結構
- Response:回應 body 的結構、狀態碼
- 錯誤:哪些錯誤應對應到哪些狀態碼
階段 1.5 — 復用分析(關鍵,不可跳過)
目標:在建立任何新東西之前,偵測該功能是否已部分或完全存在。
步驟
- 使用
@explore在*.Impl/中搜尋該功能領域的現有服務 - 搜尋候選方法,這些方法應能涵蓋所需行為的 80% 以上:
- 是否存在一個已回傳所需數據,只需簡單過濾的方法?
- 是否存在一個可透過多載來涵蓋此案例的方法?
- 在
*.API/中搜尋具有相同 request/response 格式的類似端點
決策樹
是否存在可 100% 涵蓋此案例的方法?
└── 是 → 不要建立新方法。只建立:
· 在 *.API/ 中的端點 (Function/Controller)
· 在 *.API/ 中專用的 Request/Response DTO
· Response 形狀的測試
是否存在近乎理想的方法(過濾、分頁、排序)?
├── 是 → 考慮:
│ ├── 為既有方法添加可選參數(附預設值)
│ ├── 建立簽章更明確的多載
│ └── 建立包裝既有方法的擴充方法
└── 否 → 繼續建立新方法
是否存在相關但不同的服務(例如,相似的職責)?
├── 是 → 詢問使用者:「我應該擴充 [CSXxxService] 還是建立新的 [CSYyyService]?」
└── 否 → 建立新服務
此階段的預期輸出
在繼續之前,需向使用者呈現以下三項決策之一:
- 純復用:該功能實作為既有方法的包裝器
- 擴充:為既有服務添加多載/方法
- 新建:新的服務和方法(需說明為何無法復用)
階段 1.6 — 配置分析(新增)
目標:偵測該功能是否需要可配置的參數,如果需要,應在何處定義它們。
關鍵問題
- 該功能是否具有合理的預設值而無需外部配置?
- 是否有閾值、分頁、逾時或旗標應可依環境配置?
- 是否連接到具有 URL/金鑰/密碼的外部系統?
- 是否需要功能切換以進行逐步部署?
決策樹
該功能是否需要配置?
├── 否 → 不要建立 IXxxOptions,不要碰觸 AddCustomConfiguration
└── 是 → 繼續
需要何種配置類型?
├── 連線字串或密碼
│ └── 使用 IConfiguration 與 appsettings.json 中的名稱(由 AddCustomConfiguration 管理)
├── 簡單值(閾值、逾時、旗標)
│ └── 在 *.Impl/ 中建立 IXxxOptions (interface) + 將其加入 AddCustomConfiguration<T>("key") 中
└── 值清單(允許的來源、映射)
└── 建立具有 IEnumerable<T> 類型屬性的 IXxxOptions
專案慣例
專案使用位於 IoCExtensions.cs(目前專案的第 43-45 行)中的 AddCustomConfiguration<IXxxOptions>("config-section")。介面位於 *.Impl/ 並從 *.Impl/Services/ 引用。
預期輸出
如果該功能需要配置,向使用者呈現:
- 介面名稱(例如,
IPendingPaymentOptions) - 屬性及其類型和預設值
appsettings.json中的區段(例如,"RAMBLA.PendingPayment")- 對
IoCExtensions.AddCSLevelBooking()的修改
如果不需要,請在計畫中明確說明。
階段 1.7 — 公開 API 影響分析
目標:避免破壞現有的消費者。
如果功能復用了既有方法
- 該方法是否維持相同簽章?→ 無影響
- 是否添加了可選參數?→ 驗證沒有任何呼叫點被破壞
- 是否添加了多載?→ 驗證編譯器可以無歧義地解析
- 是否重新命名?→ 使用
grep搜尋所有呼叫點並遷移
如果功能新增了服務
- 該服務是否註冊在 DI 中?→ 確保在
IoCExtensions中註冊 - 是否有任何既有服務存在潛在的循環相依?→ 驗證
如果功能新增了端點
- 路由是否與其他現有路由衝突?→ 驗證
- HTTP 方法是否符合 REST 原則?(GET 用於讀取,POST 用於變更)
階段 1.8 — 迴歸測試預檢(強制)
目標:在生成新程式碼或修改既有程式碼之前,驗證將要觸及的元件(即使是間接的)是否具有測試覆蓋率。如果沒有,則在開始實作之前建立並執行它們。
步驟
-
識別間接影響範圍:根據階段 1.5(復用 vs 擴充 vs 新建),列出:
- 其方法將被擴充或多載的既有服務
- 將被修改的既有介面
- 可能受路由/DI 影響的既有端點
- 將被編輯(而非建立)的任何既有檔案
-
對於將被觸及的每個既有元件:
- 在
src/*.Test*/**中搜尋執行它們的測試 - 如果有覆蓋率 → 可以,繼續
- 如果沒有覆蓋率 → HARD BLOCK:
- 呼叫
@refactor-worker-tests以建立當前行為的特徵化測試 - 呼叫
@refactor-verifier以確認測試通過 - 單獨提交:
test(characterization): add characterization tests for <classes>
- 呼叫
- 在
-
如果該功能是 100% 新建(不觸及現有檔案):
- 沒有迴歸影響範圍 → 跳過預檢
- 但該新功能的測試將在階段 4 中建立,並且必須在階段 5 中通過
結束標準(階段 5)
要認定功能正確,@refactor-verifier 必須確認以下通過:
- 該功能的新測試(服務 + 端點)
- 間接影響元件的既有測試(迴歸)
如果任何一項失敗,則該功能無法關閉。
階段 2 — 決策樹:建立什麼及在哪裡
套用階段 1.5(復用 / 擴充 / 新建)加上階段 1.6(配置)的決策。
對於使用復用的唯讀端點
*.API/
+ {Name}Function.cs — 新的 Function/Controller
+ {Name}Request.cs — 僅限展示層
+ {Name}Response.cs — 僅限展示層
*.Test*/
+ {Name}FunctionTests.cs — Response 形狀測試(非邏輯)
對於使用新服務的變更端點
*.Contracts/
+ ICS{Name}Service.cs — 服務介面
+ {Name}Request.cs — 如果 Request 是共享的
+ {Name}Response.cs — 如果 Response 是共享的
*.Impl/
+ CS{Name}Service.cs — 實作
+ I{Name}Options.cs — 僅當階段 1.6 偵測到配置時
*.API/
+ {Name}Function.cs — Function/Controller
+ {Name}Request.cs — 僅限該端點(如果未放在 Contracts 中)
+ {Name}Response.cs — 僅限該端點(如果未放在 Contracts 中)
*.Test*/
+ CS{Name}ServiceTests.cs — 快樂路徑 + 至少一個錯誤
對於擴充既有服務
*.Contracts/
~ ICS{Name}Service.cs — 添加方法簽章(或建立多載)
*.Impl/
~ CS{Name}Service.cs — 實作新方法/多載
*.API/
+ {Name}Function.cs — 新的 Function/Controller
+ {Name}Request.cs — 如果僅限該端點
+ {Name}Response.cs — 如果僅限該端點
*.Test*/
+ (或 ~) 服務測試 — 為新方法添加案例
階段 3 — 建立計畫
在呼叫工作單元之前,協調器向使用者呈現此計畫並等待明確確認:
[建立計畫] 功能:<名稱> — <簡短描述>
主機:<FUNCTIONS | WEBAPP>
分類:<READ-ONLY | MUTATION>
策略:<REUSE | EXTENSION | NEW>
復用分析(階段 1.5):
· 候選服務:<ICSXxxService> 或 "無"
· 候選方法:<XxxService.YyyMethod> 或 "無"
· 決策:<REUSE | EXTENSION | NEW>
配置分析(階段 1.6):
· 需要配置:<是 | 否>
· 如果需要:<IXxxOptions + appsettings 區段>
公開 API 影響(階段 1.7):
· 受影響的呼叫點:<清單> 或 "無"
· 破壞性變更:<是 | 否>
要建立的產出物:
*.Contracts/ ([N] 個檔案):
+ ICS{Name}Service.cs — 新介面(或:向 IXxx 添加方法)
+ {Name}Request.cs — 如果是共享類型
+ {Name}Response.cs — 如果是共享類型
*.Impl/ ([N] 個檔案):
+ CS{Name}Service.cs — 服務實作
+ I{Name}Options.cs — 僅當需要配置時(階段 1.6)
*.API/ ([N] 個檔案):
+ {Name}Function.cs — Azure Function(或:{Name}Controller.cs)
+ {Name}Request.cs — 如果是該端點專用類型
+ {Name}Response.cs — 如果是該端點專用類型
*.Test*/ ([N] 個檔案):
+ CS{Name}ServiceTests.cs — 服務測試(快樂路徑 + 錯誤)
+ {Name}FunctionTests.cs — 端點測試(如果適用)
對現有檔案的修改:
~ src/.../IoCExtensions.cs — 註冊服務和選項(如果適用)
執行順序:Contracts → Impl → Presentation → Tests
是否繼續?
未經使用者明確批准,不得呼叫工作單元。
階段 4 — 執行
工作單元順序
@refactor-worker-contracts— 僅當*.Contracts/中有新類型或簽章時@refactor-worker-impl— 每當*.Impl/中有程式碼時@refactor-worker-presentation— 每當有端點時@refactor-worker-tests— 總是在最後
注意:除非該功能暴露了一個新的 Service Gateway,否則不會呼叫
@refactor-worker-crosscutting。這在建立新功能時很少見;通常發生在重構中。
對每個工作單元的指示
在提示中包含:
- 完整計畫(階段 3)
- 專案模式:在建立之前讀取一個類似的檔案
- 包含預期內容的確切檔案清單
- 如果階段 1.6 偵測到配置:
IXxxOptions的路徑和配置區段
階段 5 — 驗證與提交
- 呼叫
@refactor-verifier - 如果通過 → 提議以下格式的提交:
feat(<範圍>): add <短名稱> feature - Added ICS{Name}Service interface and CS{Name}Service implementation - Added {VERB} /{route} Function/Controller - Added {Name}Request/{Name}Response contracts - Added I{Name}Options for new configuration (si aplica) - Added unit tests for CS{Name}Service - 如果失敗 → 識別失敗的層級,重新呼叫工作單元,重新驗證
各主機的模式
FUNCTIONS — Azure Functions Isolated Worker
在建立 Function 之前,讀取一個現有的 HTTP Function 並複製其風格。典型範例:
public class {Name}Function
{
private readonly I{Name}Service _{field};
public {Name}Function(I{Name}Service {field}) => _{field} = {field};
[Function("{Name}Function")]
public async Task<HttpResponseData> RunAsync(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "{route}")] HttpRequestData req)
{
var request = await req.ReadFromJsonAsync<{Name}Request>();
var result = await _{field}.ExecuteAsync(request);
var response = req.CreateResponse(HttpStatusCode.OK);
await response.WriteAsJsonAsync(result);
return response;
}
}
建立前驗證:
- 是否
AuthorizationLevel.Function或 `A