add AGENTS
This commit is contained in:
parent
b4b28eecd4
commit
7dfba5e237
|
|
@ -0,0 +1,109 @@
|
||||||
|
# Backend Agent (Golang Backend Engineer)
|
||||||
|
|
||||||
|
## Role Positioning
|
||||||
|
|
||||||
|
**Golang Backend Engineer** - Responsible for API design, server-side implementation, and ensuring high-quality, testable Golang code following Domain-Driven + go-zero style architecture.
|
||||||
|
|
||||||
|
## Core Responsibilities
|
||||||
|
|
||||||
|
1. **API Specification** - Design RESTful APIs based on the PRD, producing OpenAPI 3.0 specifications
|
||||||
|
2. **Domain Modeling** - Define domain entities, value objects, and business rules aligned with PRD and DB schema
|
||||||
|
3. **Core Implementation** - Build server-side logic following Domain-Driven architecture (pkg/domain definitions → pkg/usecase implementation → internal/logic handler → pkg/repository infrastructure)
|
||||||
|
4. **Quality Assurance (TDD)** - Implement features using Test-Driven Development to ensure high coverage and reliability
|
||||||
|
5. **Cross-Team Collaboration** - Sync with DBA Agent for schema design, UX Agent for API integration, and QA Agent for testability
|
||||||
|
|
||||||
|
## Skills Used
|
||||||
|
|
||||||
|
| Stage | Skill | Auxiliary | Input | Output |
|
||||||
|
|-------|-------|-----------|-------|--------|
|
||||||
|
| 4: API Design | `be-api-design` | `design-an-interface` | PRD | `docs/api/{date}-{feature}.yaml` |
|
||||||
|
| 5: DB Schema | Collaborate with DBA Agent | - | API spec + domain model | Alignment confirmation |
|
||||||
|
| 8: Task Breakdown | Review Orchestrator's plan | - | `./plans/{feature}.md` | Feasibility confirmation |
|
||||||
|
| 9: Implementation | `go-backend-dev` | `tdd` | Plan + API spec + DB schema | Production-ready Go code |
|
||||||
|
| 10: QA Support | Bug fix support | - | QA report | Bug fixes + regression tests |
|
||||||
|
| 11: Code Review | Respond to PR feedback | - | Review comments | Code changes |
|
||||||
|
|
||||||
|
## Working Principles
|
||||||
|
|
||||||
|
1. **API-First Design** - Define the API contract (OpenAPI) before writing implementation code
|
||||||
|
2. **Domain-Driven Architecture** - `pkg/domain/` contains pure abstractions (entities, value objects, interfaces), `pkg/usecase/` and `pkg/repository/` contain implementations, `internal/logic/` and `internal/svc/` handle wiring
|
||||||
|
3. **Test-Driven Development** - Write tests first (Red), then implement (Green), then refactor
|
||||||
|
4. **Vertical Slices** - Implement end-to-end features one slice at a time, not layer by layer
|
||||||
|
5. **Error Transparency** - Every error is checked immediately; use `fmt.Errorf` with `%w` for wrapping
|
||||||
|
6. **Interface Segregation** - Keep interfaces small (1-3 methods); define them where they are consumed
|
||||||
|
|
||||||
|
## Rollback Mechanism
|
||||||
|
|
||||||
|
```
|
||||||
|
Design Review Rejects API Spec (Stage 7)
|
||||||
|
→ Revise OpenAPI spec (skill: be-api-design + design-an-interface)
|
||||||
|
→ Coordinate with DBA Agent if schema changes needed
|
||||||
|
|
||||||
|
QA Failed (Stage 10)
|
||||||
|
→ Rollback to Stage 8 (Task Breakdown)
|
||||||
|
→ Fix bugs + add regression tests
|
||||||
|
→ Re-enter Stage 10
|
||||||
|
|
||||||
|
Code Review Failed (Stage 11)
|
||||||
|
→ Address PR feedback
|
||||||
|
→ Re-enter Stage 10 for verification
|
||||||
|
|
||||||
|
DBA/UX Conflict
|
||||||
|
→ Coordinate with DBA Agent: adjust domain model or API
|
||||||
|
→ Coordinate with UX Agent: adjust API responses or request formats
|
||||||
|
```
|
||||||
|
|
||||||
|
## Collaboration with Other Agents
|
||||||
|
|
||||||
|
```
|
||||||
|
Backend Agent ← PM Agent: Receive PRD and non-functional requirements
|
||||||
|
Backend Agent ←→ DBA Agent: Align API resources with Database Schema (Stage 5)
|
||||||
|
Backend Agent ←→ UX Agent: Ensure API responses match prototype needs (Stage 6)
|
||||||
|
Backend Agent ← Design Reviewer: Receive design feedback, revise API spec (Stage 7)
|
||||||
|
Backend Agent ← Orchestrator: Receive implementation plan (Stage 8)
|
||||||
|
Backend Agent → QA Agent: Provide testable code and API docs (Stage 10)
|
||||||
|
Backend Agent ← Code Reviewer: Implement feedback from PR reviews (Stage 11)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Decision Authority
|
||||||
|
|
||||||
|
- Define the internal technical structure of the backend (packages, layers)
|
||||||
|
- Choose specific Go libraries within the approved tech stack
|
||||||
|
- Determine the granularity of API endpoints and resource modeling
|
||||||
|
- Set the internal testing strategy (unit, integration, E2E boundaries)
|
||||||
|
- Decide on error handling patterns and response formats
|
||||||
|
|
||||||
|
## Deliverables Checklist
|
||||||
|
|
||||||
|
### Stage 4: API Design
|
||||||
|
- [ ] OpenAPI 3.0 specification saved to `docs/api/`
|
||||||
|
- [ ] All functional requirements from PRD mapped to endpoints
|
||||||
|
- [ ] `design-an-interface` alternatives documented
|
||||||
|
|
||||||
|
### Stage 5: DB Collaboration
|
||||||
|
- [ ] Domain model aligns with DB schema
|
||||||
|
- [ ] Repository interfaces feasible with proposed schema
|
||||||
|
|
||||||
|
### Stage 9: Implementation
|
||||||
|
- [ ] Domain-Driven architecture structure complete (pkg/domain, pkg/usecase, internal/logic, pkg/repository)
|
||||||
|
- [ ] All layers implemented
|
||||||
|
- [ ] Unit tests >= 80%, business logic >= 90%
|
||||||
|
- [ ] Integration tests passing for critical paths
|
||||||
|
|
||||||
|
### Stage 10-11: QA & Code Review
|
||||||
|
- [ ] All QA-reported bugs fixed with regression tests
|
||||||
|
- [ ] PR review feedback addressed
|
||||||
|
|
||||||
|
## Common Issues Handling
|
||||||
|
|
||||||
|
**Q: PRD requirements are technically infeasible?**
|
||||||
|
A: Document the constraint in the API spec, propose alternatives, escalate to PM Agent for scope adjustment.
|
||||||
|
|
||||||
|
**Q: API design conflicts with DB schema?**
|
||||||
|
A: Coordinate with DBA Agent. Prefer changing the mapping layer over altering the DB schema.
|
||||||
|
|
||||||
|
**Q: Performance bottleneck detected?**
|
||||||
|
A: Add caching (Redis), optimize queries with DBA Agent, consider async for long-running operations.
|
||||||
|
|
||||||
|
**Q: Test coverage below 80%?**
|
||||||
|
A: Prioritize usecase layer tests, use table-driven tests for edge cases, add integration tests for critical flows.
|
||||||
|
|
@ -0,0 +1,322 @@
|
||||||
|
# 實施檢查清單
|
||||||
|
|
||||||
|
本文件提供 Vibe-Kanban 工作流程的實施檢查清單,確保所有組件都已正確實作。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 檢查清單總覽
|
||||||
|
|
||||||
|
- [ ] Phase 1: 核心框架
|
||||||
|
- [ ] Phase 2: 設計階段技能
|
||||||
|
- [ ] Phase 3: 整合測試
|
||||||
|
- [ ] Phase 4: 優化
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 1: 核心框架
|
||||||
|
|
||||||
|
### 1.1 主控技能 - vibe-kanban
|
||||||
|
|
||||||
|
```
|
||||||
|
skills/vibe-kanban/
|
||||||
|
├── SKILL.md # 主技能定義
|
||||||
|
├── references/
|
||||||
|
│ └── workflow-diagram.md # 工作流程圖
|
||||||
|
└── templates/
|
||||||
|
└── state-template.yaml # 狀態模板
|
||||||
|
```
|
||||||
|
|
||||||
|
#### SKILL.md 必須包含
|
||||||
|
|
||||||
|
- [ ] 階段定義
|
||||||
|
- [ ] 狀態機邏輯
|
||||||
|
- [ ] 階段轉換條件
|
||||||
|
- [ ] 退回機制
|
||||||
|
- [ ] 狀態持久化邏輯
|
||||||
|
- [ ] 進度報告格式
|
||||||
|
- [ ] 命令列表
|
||||||
|
- [ ] `/vibe-kanban start`
|
||||||
|
- [ ] `/vibe-kanban status`
|
||||||
|
- [ ] `/vibe-kanban next`
|
||||||
|
- [ ] `/vibe-kanban back`
|
||||||
|
- [ ] `/vibe-kanban skip`
|
||||||
|
|
||||||
|
#### 狀態模板 state-template.yaml
|
||||||
|
|
||||||
|
- [ ] 專案名稱欄位
|
||||||
|
- [ ] 當前階段欄位
|
||||||
|
- [ ] 歷史記錄欄位
|
||||||
|
- [ ] 產出文件欄位
|
||||||
|
- [ ] 阻塞原因欄位
|
||||||
|
- [ ] 下一步動作欄位
|
||||||
|
|
||||||
|
### 1.2 PRD 技能 - pm-prd
|
||||||
|
|
||||||
|
```
|
||||||
|
skills/pm-prd/
|
||||||
|
├── SKILL.md
|
||||||
|
└── templates/
|
||||||
|
└── prd-template.md
|
||||||
|
```
|
||||||
|
|
||||||
|
#### SKILL.md 必須包含
|
||||||
|
|
||||||
|
- [ ] 輸入格式定義
|
||||||
|
- [ ] 輸出格式定義
|
||||||
|
- [ ] 功能性需求模板
|
||||||
|
- [ ] 非功能性需求模板
|
||||||
|
- [ ] 驗收標準模板
|
||||||
|
- [ ] 優先級排序邏輯
|
||||||
|
- [ ] 人類審核流程
|
||||||
|
|
||||||
|
#### PRD 模板 prd-template.md
|
||||||
|
|
||||||
|
- [ ] Metadata 區塊
|
||||||
|
- [ ] 背景區塊
|
||||||
|
- [ ] 功能性需求區塊 (FR-001, FR-002, ...)
|
||||||
|
- [ ] 非功能性需求區塊 (NFR-001, NFR-002, ...)
|
||||||
|
- [ ] 驗收標準區塊 (AC-001, AC-002, ...)
|
||||||
|
- [ ] 排期建議區塊
|
||||||
|
- [ ] 依賴區塊
|
||||||
|
- [ ] 風險評估區塊
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 2: 設計階段技能
|
||||||
|
|
||||||
|
### 2.1 API 設計技能 - be-api-design
|
||||||
|
|
||||||
|
```
|
||||||
|
skills/be-api-design/
|
||||||
|
├── SKILL.md
|
||||||
|
└── templates/
|
||||||
|
└── openapi-template.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
#### SKILL.md 必須包含
|
||||||
|
|
||||||
|
- [ ] RESTful API 設計原則
|
||||||
|
- [ ] OpenAPI 3.0 規格產出
|
||||||
|
- [ ] 請求/回應 Schema 定義
|
||||||
|
- [ ] 錯誤處理設計
|
||||||
|
- [ ] 安全性考量清單
|
||||||
|
- [ ] 效能考量清單
|
||||||
|
- [ ] 人類審核流程
|
||||||
|
|
||||||
|
#### OpenAPI 模板 openapi-template.yaml
|
||||||
|
|
||||||
|
- [ ] info 區塊 (標題、版本、描述)
|
||||||
|
- [ ] paths 區塊 (端點定義)
|
||||||
|
- [ ] components/schemas 區塊 (資料模型)
|
||||||
|
- [ ] responses 區塊 (回應格式)
|
||||||
|
- [ ] securitySchemes 區塊 (安全機制)
|
||||||
|
|
||||||
|
### 2.2 資料庫設計技能- dba-schema
|
||||||
|
|
||||||
|
```
|
||||||
|
skills/dba-schema/
|
||||||
|
├── SKILL.md
|
||||||
|
└── templates/
|
||||||
|
└── schema-template.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
#### SKILL.md 必須包含
|
||||||
|
|
||||||
|
- [ ] 資料正規化原則
|
||||||
|
- [ ] 索引策略
|
||||||
|
- [ ] 外鍵關聯設計
|
||||||
|
- [ ] 遷移計畫格式
|
||||||
|
- [ ] 效能優化建議
|
||||||
|
- [ ] 人類審核流程
|
||||||
|
|
||||||
|
#### Schema 模板 schema-template.sql
|
||||||
|
|
||||||
|
- [ ] Migration 標頭
|
||||||
|
- [ ] Table 定義
|
||||||
|
- [ ] Index 定義
|
||||||
|
- [ ] Constraint 定義
|
||||||
|
- [ ] Trigger 定義
|
||||||
|
- [ ] Comment 定義
|
||||||
|
|
||||||
|
### 2.3 UX 原型技能 - ux-prototype
|
||||||
|
|
||||||
|
```
|
||||||
|
skills/ux-prototype/
|
||||||
|
├── SKILL.md
|
||||||
|
└── templates/
|
||||||
|
├── user-flow-template.md
|
||||||
|
└── component-mapping-template.md
|
||||||
|
```
|
||||||
|
|
||||||
|
#### SKILL.md 必須包含
|
||||||
|
|
||||||
|
- [ ] 使用者流程圖產出
|
||||||
|
- [ ] 線框圖描述格式
|
||||||
|
- [ ] 元件與 API 對應邏輯
|
||||||
|
- [ ] 原型工具建議
|
||||||
|
- [ ] 人類審核流程
|
||||||
|
|
||||||
|
#### 使用者流程模板 user-flow-template.md
|
||||||
|
|
||||||
|
- [ ] Mermaid 流程圖
|
||||||
|
- [ ] 頁面清單表格
|
||||||
|
- [ ] 頁面 URL 對應
|
||||||
|
|
||||||
|
#### 元件對應模板 component-mapping-template.md
|
||||||
|
|
||||||
|
- [ ] 頁面元件列表
|
||||||
|
- [ ] 元件資料來源
|
||||||
|
- [ ] API 端點對應
|
||||||
|
- [ ] 快取策略建議
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 3: 整合測試
|
||||||
|
|
||||||
|
### 3.1 端到端流程測試
|
||||||
|
|
||||||
|
- [ ] 測試案例 1: 完整流程從頭到尾
|
||||||
|
- [Brainstorming → CEO Review → PRD → ... → Deploy]
|
||||||
|
- [ ] 測試案例 2: CEO 審核退回
|
||||||
|
- [Brainstorming → CEO Review (失敗) → Brainstorming]
|
||||||
|
- [ ] 測試案例 3: QA 失敗退回
|
||||||
|
- [所有階段 → QA (失敗) → Task Breakdown]
|
||||||
|
- [ ] 測試案例 4: PR 審核失敗退回
|
||||||
|
- [所有階段 → PR Review (失敗) → Task Breakdown]
|
||||||
|
|
||||||
|
### 3.2 狀態持久化測試
|
||||||
|
|
||||||
|
- [ ] 測試狀態檔案建立
|
||||||
|
- [ ] 測試狀態檔案更新
|
||||||
|
- [ ] 測試狀態檔案恢復
|
||||||
|
- [ ] 測試狀態檔案損壞處理
|
||||||
|
|
||||||
|
### 3.3 退回機制測試
|
||||||
|
|
||||||
|
- [ ] 測試退回到上一階段
|
||||||
|
- [ ] 測試退回到特定階段
|
||||||
|
- [ ] 測試退回後的產出清理
|
||||||
|
- [ ] 測試退回後重新執行
|
||||||
|
|
||||||
|
### 3.4 併發測試
|
||||||
|
|
||||||
|
- [ ] 測試多專案併發執行
|
||||||
|
- [ ] 測試鎖機制
|
||||||
|
- [ ] 測試資源競爭
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 4: 優化
|
||||||
|
|
||||||
|
### 4.1 效能優化
|
||||||
|
|
||||||
|
- [ ] 階段轉換時間優化
|
||||||
|
- [ ] 檔案讀寫優化
|
||||||
|
- [ ] 快取機制實作
|
||||||
|
|
||||||
|
### 4.2 錯誤處理優化
|
||||||
|
|
||||||
|
- [ ] 友善錯誤訊息
|
||||||
|
- [ ] 錯誤恢復機制
|
||||||
|
- [ ] 錯誤日誌記錄
|
||||||
|
|
||||||
|
### 4.3 使用者體驗優化
|
||||||
|
|
||||||
|
- [ ] 進度視覺化
|
||||||
|
- [ ] 預估時間顯示
|
||||||
|
- [ ] 歷史記錄瀏覽
|
||||||
|
|
||||||
|
### 4.4 文件完善
|
||||||
|
|
||||||
|
- [ ] 使用者指南
|
||||||
|
- [ ] 開發者文件
|
||||||
|
- [ ] API 文件
|
||||||
|
- [ ] 最佳實踐指南
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 測試矩陣
|
||||||
|
|
||||||
|
### 功能測試
|
||||||
|
|
||||||
|
| 功能 | 測試案例 | 預期結果 | 實際結果 | 狀態 |
|
||||||
|
|------|---------|---------|---------|------|
|
||||||
|
| 開始新專案 | 輸入專案名稱 | 建立狀態檔案 | | [ ] |
|
||||||
|
| 查看狀態 | 執行status | 顯示當前階段 | | [ ] |
|
||||||
|
| 前往下一階段 | 執行 next | 狀態更新 | | [ ] |
|
||||||
|
| 退回上一階段 | 執行 back | 狀態回滾 | | [ ] |
|
||||||
|
| 跳過階段 | 執行 skip | 狀態跳過 | | [ ] |
|
||||||
|
|
||||||
|
### 整合測試
|
||||||
|
|
||||||
|
| 整合點 | 測試案例 | 預期結果 | 實際結果 | 狀態 |
|
||||||
|
|--------|---------|---------|---------|------|
|
||||||
|
| PM → CEO | PRD 審核通過 | 進入 API 設計 | | [ ] |
|
||||||
|
| CEO 失敗 | PRD 審核失敗 | 退回Brainstorming | | [ ] |
|
||||||
|
| QA 失敗 | 測試不通過 | 退回 Task Breakdown | | [ ] |
|
||||||
|
| Deploy | 部署成功 | 專案完成 | | [ ] |
|
||||||
|
|
||||||
|
### 效能測試
|
||||||
|
|
||||||
|
| 指標 | 目標 | 實際 | 狀態 |
|
||||||
|
|------|------|------|------|
|
||||||
|
| 階段轉換時間 | < 5 秒 | | [ ] |
|
||||||
|
| 狀態載入時間 | < 1 秒 | | [ ] |
|
||||||
|
|檔案寫入時間 | < 2 秒 | | [ ] |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 部署檢查清單
|
||||||
|
|
||||||
|
### 部署前
|
||||||
|
|
||||||
|
- [ ] 所有測試通過
|
||||||
|
- [ ] 文件完整
|
||||||
|
- [ ] 版本號更新
|
||||||
|
- [ ] CHANGELOG 更新
|
||||||
|
|
||||||
|
### 部署中
|
||||||
|
|
||||||
|
- [ ] 備份現有版本
|
||||||
|
- [ ] 部署新版本
|
||||||
|
- [ ] 驗證部署成功
|
||||||
|
|
||||||
|
### 部署後
|
||||||
|
|
||||||
|
- [ ] 監控錯誤日誌
|
||||||
|
- [ ] 驗證功能正常
|
||||||
|
- [ ] 更新使用文件
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 維護檢查清單
|
||||||
|
|
||||||
|
### 每週
|
||||||
|
|
||||||
|
- [ ] 檢查錯誤日誌
|
||||||
|
- [ ] 檢查效能指標
|
||||||
|
- [ ] 檢查使用者反饋
|
||||||
|
|
||||||
|
### 每月
|
||||||
|
|
||||||
|
- [ ] 更新技能版本
|
||||||
|
- [ ] 審核技術債
|
||||||
|
- [ ] 優化流程
|
||||||
|
|
||||||
|
### 每季
|
||||||
|
|
||||||
|
- [ ] 全面測試回歸
|
||||||
|
- [ ] 安全性審核
|
||||||
|
- [ ] 架構審核
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 簽核記錄
|
||||||
|
|
||||||
|
| 階段 | 審核人 | 日期 | 簽核 |
|
||||||
|
|------|-------|------|------|
|
||||||
|
| Phase 1 | | | [ ] |
|
||||||
|
| Phase 2 | | | [ ] |
|
||||||
|
| Phase 3 | | | [ ] |
|
||||||
|
| Phase 4 | | | [ ] |
|
||||||
|
| 最終審核 | | | [ ] |
|
||||||
|
|
@ -0,0 +1,197 @@
|
||||||
|
# Vibe-Kanban 快速開始指南
|
||||||
|
|
||||||
|
本指南幫助您快速理解如何使用 Vibe-Kanban 工作流程。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 核心概念
|
||||||
|
|
||||||
|
### 什麼是 Vibe-Kanban?
|
||||||
|
|
||||||
|
Vibe-Kanban 是一個由 AI 代理協作的軟體開發工作流程系統。每個開發階段都由特定的 AI 代理負責,從需求探索到部署全自動化。
|
||||||
|
|
||||||
|
### 核心角色
|
||||||
|
|
||||||
|
| 角色 | 負責階段 | AI 技能 |
|
||||||
|
|------|---------|---------|
|
||||||
|
| PM (產品經理) | 需求探索、PRD 撰寫 | brainstorming,pm-prd |
|
||||||
|
| CEO (商業決策) | 商業價值審核 | plan-ceo-review |
|
||||||
|
| Backend Engineer | API 設計 | be-api-design |
|
||||||
|
| DBA | 資料庫規劃 | dba-schema |
|
||||||
|
| UX Designer | 原型設計 | ux-prototype |
|
||||||
|
| Design Reviewer | 設計審核 | design-review |
|
||||||
|
| QA Engineer | 測試驗收 | qa |
|
||||||
|
| OPS (運維) | 部署 | land-and-deploy |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 使用方式
|
||||||
|
|
||||||
|
### 1. 開始新專案
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/vibe-kanban start "使用者認證功能"
|
||||||
|
```
|
||||||
|
|
||||||
|
系統會自動:
|
||||||
|
1. 建立專案狀態檔案
|
||||||
|
2. 觸發 PM 進行需求探索
|
||||||
|
3. 進入 Brainstorming 階段
|
||||||
|
|
||||||
|
### 2. 查看當前狀態
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/vibe-kanban status
|
||||||
|
```
|
||||||
|
|
||||||
|
輸出範例:
|
||||||
|
```
|
||||||
|
╔══════════════════════════════════════════════════════════════╗
|
||||||
|
║ VIBE-KANBAN 狀態 ║
|
||||||
|
╠══════════════════════════════════════════════════════════════╣
|
||||||
|
║ 專案: feature-user-auth ║
|
||||||
|
║ 當前階段: CEO_REVIEW (CEO 商業價值審核) ║
|
||||||
|
║ 狀態: IN_PROGRESS ║
|
||||||
|
║ ║
|
||||||
|
║ 下一步: 等待 CEO 審核通過 ║
|
||||||
|
╚══════════════════════════════════════════════════════════════╝
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 前往下一階段
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/vibe-kanban next
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 退回修改
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/vibe-kanban back "設計與需求不符,需要重新定義"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 工作流程總覽
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────┐ ┌──────────┐ ┌─────────┐ ┌───────────┐
|
||||||
|
│Brainstorm│ ──▶│CEO Review│ ──▶│ PRD │ ──▶│ API Design │
|
||||||
|
└─────────┘ └──────────┘ └─────────┘ └───────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────┐ ┌──────────┐ ┌──────────┐ ┌───────────┐
|
||||||
|
│ Deploy │ ◀──│PR Review │◀───│ QA │◀───│DB Schema │
|
||||||
|
└─────────┘ └──────────┘ └──────────┘ └───────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 各階段產出
|
||||||
|
|
||||||
|
### 階段 1-2: 需求探索
|
||||||
|
- **產出**: `docs/brainstorm/{date}-{feature}.md`
|
||||||
|
- **內容**: 需求分析、使用者故事、技術方案
|
||||||
|
|
||||||
|
### 階段 3: PRD 撰寫
|
||||||
|
- **產出**: `docs/prd/{date}-{feature}.md`
|
||||||
|
- **內容**: 功能性需求、非功能性需求、驗收標準
|
||||||
|
|
||||||
|
### 階段 4: API 設計
|
||||||
|
- **產出**: `docs/api/{date}-{feature}.yaml`
|
||||||
|
- **內容**: OpenAPI 3.0 規格文件
|
||||||
|
|
||||||
|
### 階段 5: 資料庫規劃
|
||||||
|
- **產出**: `docs/db/{date}-{feature}.sql`
|
||||||
|
- **內容**: 資料表定義、索引、遷移計畫
|
||||||
|
|
||||||
|
### 階段 6: UX 原型
|
||||||
|
- **產出**: `docs/design/{date}-{feature}/`
|
||||||
|
- **內容**: 使用者流程、線框圖、原型連結
|
||||||
|
|
||||||
|
### 階段 7: 設計審核
|
||||||
|
- **產出**: `docs/design/{date}-{feature}/review-report.md`
|
||||||
|
- **內容**: 審核報告、修改建議
|
||||||
|
|
||||||
|
### 階段 8: 任務拆分
|
||||||
|
- **產出**: `.gstack/kanban/{project}/tasks.md`
|
||||||
|
- **內容**: 前後端任務分配
|
||||||
|
|
||||||
|
### 階段 9: 實作
|
||||||
|
- **產出**: 程式碼
|
||||||
|
- **內容**: 功能實作、單元測試
|
||||||
|
|
||||||
|
### 階段 10: QA 測試
|
||||||
|
- **產出**: `.gstack/qa-reports/qa-report-{date}.md`
|
||||||
|
- **內容**: 測試報告、Bug 列表
|
||||||
|
|
||||||
|
### 階段 11: PR 審核
|
||||||
|
- **產出**: PR 描述、審核報告
|
||||||
|
- **內容**: 代碼變更說明
|
||||||
|
|
||||||
|
### 階段 12: 部署
|
||||||
|
- **產出**: `.gstack/deploy-reports/deploy-report-{date}.md`
|
||||||
|
- **內容**: 部署日誌、驗證結果
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
常見問題
|
||||||
|
|
||||||
|
### Q: 如果某個階段的產出不滿意怎麼辦?
|
||||||
|
|
||||||
|
A: 使用 `/vibe-kanban back "原因"` 退回到上一階段重新處理。
|
||||||
|
|
||||||
|
### Q: 可以跳過某些階段嗎?
|
||||||
|
|
||||||
|
A: 使用 `/vibe-kanban skip`,但需要確認跳過的原因和風險。
|
||||||
|
|
||||||
|
### Q: 如何查看歷史記錄?
|
||||||
|
|
||||||
|
A: 查看 `.gstack/kanban/{project}/` 目錄下的 `state.yaml` 檔案。
|
||||||
|
|
||||||
|
### Q: 多人協作時如何同步狀態?
|
||||||
|
|
||||||
|
A: 將 `.gstack/kanban/` 目錄加入版本控制,團隊成員可以共享狀態。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 最佳實踐
|
||||||
|
|
||||||
|
1. **每個階段都應該產出文件** - 確保可追溯性
|
||||||
|
2. **及時退回修改** - 發現問題立即退回,不要累積技術債
|
||||||
|
3. **QA 前確保驗收標準完整** - 測試案例來自驗收標準
|
||||||
|
4. **部署前確認所有測試通過** - 自動化測試是門檻
|
||||||
|
5. **保存所有產出文件** - 方便日後參考和審計
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 故障排除
|
||||||
|
|
||||||
|
### 問題: 狀態檔案損壞
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 重置狀態檔案
|
||||||
|
rm .gstack/kanban/{project}/state.yaml
|
||||||
|
/vibe-kanban start "{project-name}"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 問題: 階段卡住無法前進
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 強制前往下一階段
|
||||||
|
/vibe-kanban next --force
|
||||||
|
```
|
||||||
|
|
||||||
|
### 問題: 需要回到特定階段
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 跳轉到特定階段
|
||||||
|
/vibe-kanban goto PRD
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 下一步
|
||||||
|
|
||||||
|
1. 查看 [VIBE_KANBAN_PLAN.md](VIBE_KANBAN_PLAN.md) 了解詳細設計
|
||||||
|
2. 查看 [IMPLEMENTATION_CHECKLIST.md](IMPLEMENTATION_CHECKLIST.md) 了解實施步驟
|
||||||
|
3. 開始使用 `/vibe-kanban start "您的功能名稱"`
|
||||||
|
|
@ -0,0 +1,500 @@
|
||||||
|
# 技能依賴圖
|
||||||
|
|
||||||
|
本文件描述 Vibe-Kanban 工作流程中各技能之間的依賴關係。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 依賴圖總覽
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ 技能依賴關係圖 │
|
||||||
|
└─────────────────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
┌──────────────┐
|
||||||
|
│ 使用者 │
|
||||||
|
│ 輸入想法 │
|
||||||
|
└──────┬───────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌────────────────────────────────┐
|
||||||
|
│ │
|
||||||
|
│ vibe-kanban (主控) │
|
||||||
|
│ │
|
||||||
|
└────────────────┬───────────────┘
|
||||||
|
│
|
||||||
|
┌───────────────────────────┼───────────────────────────┐
|
||||||
|
│ │ │
|
||||||
|
▼ ▼ ▼
|
||||||
|
┌─────────────────┐ ┌─────────────────────────┐ ┌─────────────────┐
|
||||||
|
│ brainstorming │ │ plan-ceo-review │ │ pm-prd │
|
||||||
|
│ │ │ (現有技能) │ │ (新建技能) │
|
||||||
|
│ (現有技能) │ │ │ │ │
|
||||||
|
└────────┬────────┘ └────────────┬────────────┘ └────────┬────────┘
|
||||||
|
│ │ │
|
||||||
|
└─────────────┬─────────────┘ │
|
||||||
|
│ │
|
||||||
|
▼ │
|
||||||
|
┌─────────────────┐ │
|
||||||
|
│ ceo-review │◀──────────────────────────────┘
|
||||||
|
│ output │
|
||||||
|
└────────┬────────┘│
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌────────────────────────────┐
|
||||||
|
│ │
|
||||||
|
│be-api-design (新建技能) │
|
||||||
|
│ │
|
||||||
|
└─────────────┬──────────────┘│
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌────────────────────────────┐
|
||||||
|
│ │
|
||||||
|
│ dba-schema (新建技能) │
|
||||||
|
│ │
|
||||||
|
└─────────────┬──────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌────────────────────────────┐
|
||||||
|
│ │
|
||||||
|
│ ux-prototype (新建技能) │
|
||||||
|
│ │
|
||||||
|
└─────────────┬──────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌────────────────────────────┐
|
||||||
|
│ │
|
||||||
|
│ design-review (現有技能) │
|
||||||
|
│ │
|
||||||
|
└─────────────┬──────────────┘
|
||||||
|
│
|
||||||
|
│ 失敗退回 PRD
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────┐
|
||||||
|
│ │
|
||||||
|
│ dispatching-parallel-agents (現有技能) │
|
||||||
|
│ │
|
||||||
|
└────────────────┬────────────────────────┘│
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌──────────────┐
|
||||||
|
│ │
|
||||||
|
│ 前端實作 │
|
||||||
|
│ │
|
||||||
|
└──────┬───────┘
|
||||||
|
│ ┌──────────────┐
|
||||||
|
│ │ │
|
||||||
|
└───────▶│ 後端實作 │
|
||||||
|
│ │
|
||||||
|
└──────┬───────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌────────────────────────────────┐
|
||||||
|
│ │
|
||||||
|
│ qa (現有技能) │
|
||||||
|
│ │
|
||||||
|
└─────────────┬──────────────────┘
|
||||||
|
│
|
||||||
|
│ 失敗退回 Task Breakdown
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌────────────────────────────────┐
|
||||||
|
│ │
|
||||||
|
│ review (現有技能) │
|
||||||
|
│ │
|
||||||
|
└─────────────┬──────────────────┘
|
||||||
|
│
|
||||||
|
│ 失敗退回 Task Breakdown
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌────────────────────────────────┐
|
||||||
|
│ │
|
||||||
|
│ land-and-deploy (現有技能) │
|
||||||
|
│ │
|
||||||
|
└────────────────┬───────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌──────────┐
|
||||||
|
│ │
|
||||||
|
│ 完成 │
|
||||||
|
│ │
|
||||||
|
└──────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 技能分類
|
||||||
|
|
||||||
|
### 現有技能 (可直接使用)
|
||||||
|
|
||||||
|
| 技能名稱 | 位置 | 用途 |
|
||||||
|
|---------|------|------|
|
||||||
|
| `brainstorming` | skills/ | PM 需求探索 |
|
||||||
|
| `plan-ceo-review` | gstack/| CEO 商業價值審核 |
|
||||||
|
| `design-review` | gstack/| 設計審核 |
|
||||||
|
| `dispatching-parallel-agents` | skills/ | 任務平行拆分 |
|
||||||
|
| `qa` | gstack/ | QA 測試驗收 |
|
||||||
|
| `review` | gstack/ | PR 代碼審核 |
|
||||||
|
| `land-and-deploy` | gstack/ | 部署流程 |
|
||||||
|
|
||||||
|
### 新建技能 (需要實作)
|
||||||
|
|
||||||
|
| 技能名稱 | 位置 | 用途 |
|
||||||
|
|---------|------|------|
|
||||||
|
| `vibe-kanban`| skills/ | 主控協調器 |
|
||||||
|
| `pm-prd` | skills/ | PRD 撰寫 |
|
||||||
|
| `be-api-design` | skills/ | API 設計 |
|
||||||
|
| `dba-schema` | skills/ | 資料庫規劃 |
|
||||||
|
| `ux-prototype` | skills/ | UX 原型 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 詳細依賴關係
|
||||||
|
|
||||||
|
### Stage 1: Brainstorming
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
技能: brainstorming
|
||||||
|
類型: 現有
|
||||||
|
輸入:
|
||||||
|
- 使用者輸入的想法
|
||||||
|
- 專案上下文 (CLAUDE.md, README.md 等)
|
||||||
|
輸出:
|
||||||
|
- docs/brainstorm/{date}-{feature}.md
|
||||||
|
依賴: 無
|
||||||
|
觸發: vibe-kanban (主控)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stage 2: CEO Review
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
技能: plan-ceo-review
|
||||||
|
類型: 現有
|
||||||
|
輸入:
|
||||||
|
- docs/brainstorm/{date}-{feature}.md
|
||||||
|
輸出:
|
||||||
|
- docs/ceo-review/{date}-{feature}.md
|
||||||
|
- 審核報告
|
||||||
|
依賴:
|
||||||
|
- brainstorming (必須完成)
|
||||||
|
觸發: vibe-kanban (主控)
|
||||||
|
退回: 可退回 brainstorming
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stage 3: PRD
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
技能: pm-prd
|
||||||
|
類型: 新建
|
||||||
|
輸入:
|
||||||
|
- docs/brainstorm/{date}-{feature}.md
|
||||||
|
- docs/ceo-review/{date}-{feature}.md
|
||||||
|
輸出:
|
||||||
|
- docs/prd/{date}-{feature}.md
|
||||||
|
依賴:
|
||||||
|
- brainstorming (必須完成)
|
||||||
|
- plan-ceo-review (必須通過)
|
||||||
|
觸發: vibe-kanban (主控)
|
||||||
|
退回: 可退回 ceo-review
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stage 4: API Design
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
技能: be-api-design
|
||||||
|
類型: 新建
|
||||||
|
輸入:
|
||||||
|
- docs/prd/{date}-{feature}.md
|
||||||
|
輸出:
|
||||||
|
- docs/api/{date}-{feature}.yaml
|
||||||
|
依賴:
|
||||||
|
- pm-prd (必須完成)
|
||||||
|
觸發: vibe-kanban (主控)
|
||||||
|
退回: 可退回 pm-prd
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stage 5: DB Schema
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
技能: dba-schema
|
||||||
|
類型: 新建
|
||||||
|
輸入:
|
||||||
|
- docs/api/{date}-{feature}.yaml
|
||||||
|
- docs/prd/{date}-{feature}.md
|
||||||
|
輸出:
|
||||||
|
- docs/db/{date}-{feature}.sql
|
||||||
|
- docs/db/migrations/{version}-{description}.sql
|
||||||
|
依賴:
|
||||||
|
- be-api-design (必須完成)
|
||||||
|
觸發: vibe-kanban (主控)
|
||||||
|
退回: 可退回 be-api-design
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stage 6: UX Prototype
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
技能: ux-prototype
|
||||||
|
類型: 新建
|
||||||
|
輸入:
|
||||||
|
- docs/prd/{date}-{feature}.md
|
||||||
|
- docs/api/{date}-{feature}.yaml
|
||||||
|
- docs/db/{date}-{feature}.sql
|
||||||
|
輸出:
|
||||||
|
- docs/design/{date}-{feature}/user-flow.md
|
||||||
|
- docs/design/{date}-{feature}/wireframes/
|
||||||
|
- docs/design/{date}-{feature}/prototype-link.md
|
||||||
|
依賴:
|
||||||
|
- dba-schema (必須完成)
|
||||||
|
觸發: vibe-kanban (主控)
|
||||||
|
退回: 可退回 dba-schema
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stage 7: Design Review
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
技能: design-review
|
||||||
|
類型: 現有
|
||||||
|
輸入:
|
||||||
|
- docs/design/{date}-{feature}/
|
||||||
|
輸出:
|
||||||
|
- docs/design/{date}-{feature}/review-report.md
|
||||||
|
依賴:
|
||||||
|
- ux-prototype (必須完成)
|
||||||
|
觸發: vibe-kanban (主控)
|
||||||
|
退回: 可退回 pm-prd (設計與需求不符)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stage 8: Task Breakdown
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
技能: dispatching-parallel-agents-doc
|
||||||
|
類型: 現有
|
||||||
|
輸入:
|
||||||
|
- docs/prd/{date}-{feature}.md
|
||||||
|
- docs/api/{date}-{feature}.yaml
|
||||||
|
- docs/design/{date}-{feature}/
|
||||||
|
輸出:
|
||||||
|
- .gstack/kanban/{project}/tasks.md
|
||||||
|
- 任務分配
|
||||||
|
依賴:
|
||||||
|
- design-review (必須通過)
|
||||||
|
觸發: vibe-kanban (主控)
|
||||||
|
退回: 無
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stage 9: Implementation
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
技能: 無 (手動或使用 writing-plans)
|
||||||
|
類型: 手動
|
||||||
|
輸入:
|
||||||
|
- .gstack/kanban/{project}/tasks.md
|
||||||
|
輸出:
|
||||||
|
- 程式碼
|
||||||
|
- 單元測試
|
||||||
|
依賴:
|
||||||
|
- dispatching-parallel-agents-doc (必須完成)
|
||||||
|
觸發: 開發者手動
|
||||||
|
退回: 無
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stage 10: QA
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
技能: qa
|
||||||
|
類型: 現有
|
||||||
|
輸入:
|
||||||
|
- 程式碼
|
||||||
|
- docs/prd/{date}-{feature}.md (驗收標準)
|
||||||
|
輸出:
|
||||||
|
- .gstack/qa-reports/qa-report-{date}.md
|
||||||
|
依賴:
|
||||||
|
-實作 (必須完成)
|
||||||
|
觸發: vibe-kanban (主控)
|
||||||
|
退回: 可退回 dispatching-parallel-agents-doc (步驟四)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stage 11: PR Review
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
技能: review
|
||||||
|
類型: 現有
|
||||||
|
輸入:
|
||||||
|
- PR (程式碼變更)
|
||||||
|
輸出:
|
||||||
|
-PR 審核報告
|
||||||
|
依賴:
|
||||||
|
- qa (必須通過)
|
||||||
|
觸發: vibe-kanban (主控)
|
||||||
|
退回: 可退回 dispatching-parallel-agents-doc (步驟四)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stage 12: Deploy
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
技能: land-and-deploy
|
||||||
|
類型: 現有
|
||||||
|
輸入:
|
||||||
|
- 已核准的 PR
|
||||||
|
輸出:
|
||||||
|
- .gstack/deploy-reports/deploy-report-{date}.md
|
||||||
|
依賴:
|
||||||
|
- review (必須通過)
|
||||||
|
觸發: vibe-kanban (主控)
|
||||||
|
退回: 無 (部署失敗可重新部署)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 退回路徑總覽
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
退回路徑:
|
||||||
|
- from: CEO_REVIEW
|
||||||
|
to: BRAINSTORM
|
||||||
|
reason: "商業價值不足或需求不清"
|
||||||
|
|
||||||
|
- from: PRD
|
||||||
|
to: CEO_REVIEW
|
||||||
|
reason: "PRD 與 CEO 審核不符"
|
||||||
|
|
||||||
|
- from: API_DESIGN
|
||||||
|
to: PRD
|
||||||
|
reason: "API 設計與需求不符"
|
||||||
|
|
||||||
|
- from: DB_SCHEMA
|
||||||
|
to: API_DESIGN
|
||||||
|
reason: "資料結構與 API 衝突"
|
||||||
|
|
||||||
|
- from: UX_PROTOTYPE
|
||||||
|
to: DB_SCHEMA
|
||||||
|
reason: "原型與資料結構不符"
|
||||||
|
|
||||||
|
- from: DESIGN_REVIEW
|
||||||
|
to: PRD
|
||||||
|
reason: "設計與需求不符"
|
||||||
|
|
||||||
|
- from: QA
|
||||||
|
to: TASK_BREAKDOWN
|
||||||
|
reason: "測試不通過"
|
||||||
|
|
||||||
|
- from: PR_REVIEW
|
||||||
|
to: TASK_BREAKDOWN
|
||||||
|
reason: "代碼審核不通過"
|
||||||
|
|
||||||
|
- from: DEPLOY
|
||||||
|
to: IMPLEMENTATION
|
||||||
|
reason: "部署失敗"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 檔案流向圖
|
||||||
|
|
||||||
|
```
|
||||||
|
使用者輸入
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
docs/brainstorm/{date}-{feature}.md
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
docs/ceo-review/{date}-{feature}.md
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
docs/prd/{date}-{feature}.md
|
||||||
|
│
|
||||||
|
├──▶ docs/api/{date}-{feature}.yaml
|
||||||
|
│ │
|
||||||
|
│ └──▶ docs/db/{date}-{feature}.sql
|
||||||
|
│ │
|
||||||
|
│ └──▶ docs/design/{date}-{feature}/
|
||||||
|
│ │
|
||||||
|
│ └──▶ docs/design/{date}-{feature}/review-report.md
|
||||||
|
│
|
||||||
|
└──▶ .gstack/kanban/{project}/tasks.md
|
||||||
|
│
|
||||||
|
└──▶ 程式碼
|
||||||
|
│
|
||||||
|
├──▶ .gstack/qa-reports/qa-report-{date}.md
|
||||||
|
│
|
||||||
|
├──▶ PR (代碼變更)
|
||||||
|
│
|
||||||
|
└──▶ .gstack/deploy-reports/deploy-report-{date}.md
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 版本控制建議
|
||||||
|
|
||||||
|
### 應納入版本控制的檔案
|
||||||
|
|
||||||
|
```
|
||||||
|
.gstack/kanban/{project}/state.yaml # 工作流狀態
|
||||||
|
docs/brainstorm/ # 需求探索文件
|
||||||
|
docs/prd/ # PRD 文件
|
||||||
|
docs/api/ # API 規格
|
||||||
|
docs/db/ # 資料庫設計
|
||||||
|
docs/design/ # 設計文件
|
||||||
|
.gstack/qa-reports/ # QA 報告
|
||||||
|
.gstack/deploy-reports/ # 部署報告
|
||||||
|
```
|
||||||
|
|
||||||
|
### 不應納入版本控制的檔案
|
||||||
|
|
||||||
|
```
|
||||||
|
.gstack/tmp/ # 暫存檔案
|
||||||
|
node_modules/ # 依賴
|
||||||
|
*.log # 日誌檔案
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 授權和稽核
|
||||||
|
|
||||||
|
### 每個階段的授權需求
|
||||||
|
|
||||||
|
| 階段 | 需要授權 | 授權者 |
|
||||||
|
|------|---------|--------|
|
||||||
|
| BRAINSTORM | 否 | - |
|
||||||
|
| CEO_REVIEW | 是 | CEO/決策者 |
|
||||||
|
| PRD | 是 | PM |
|
||||||
|
| API_DESIGN | 是 | 後端負責人 |
|
||||||
|
| DB_SCHEMA | 是 | DBA |
|
||||||
|
| UX_PROTOTYPE | 是 | UX 設計師 |
|
||||||
|
| DESIGN_REVIEW | 是 | 設計審核者 |
|
||||||
|
| TASK_BREAKDOWN | 否 | - |
|
||||||
|
| IMPLEMENTATION | 否 | - |
|
||||||
|
| QA | 否 | - |
|
||||||
|
| PR_REVIEW | 是 | 代碼審核者 |
|
||||||
|
| DEPLOY | 否 | - |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 效能考量
|
||||||
|
|
||||||
|
### 平行執行機會
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
可平行執行:
|
||||||
|
- API_DESIGN + DB_SCHEMA (可同時進行)
|
||||||
|
- 前端實作 + 後端實作 (可同時進行)
|
||||||
|
|
||||||
|
不可平行執行:
|
||||||
|
- BRAINSTORM → CEO_REVIEW (必須順序)
|
||||||
|
- PRD → API_DESIGN (必須順序)
|
||||||
|
- QA → PR_REVIEW → DEPLOY (必須順序)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 快取策略
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
可快取:
|
||||||
|
- 專案上下文 (CLAUDE.md, README.md)
|
||||||
|
- 現有 API 規格
|
||||||
|
- 現有資料庫 Schema
|
||||||
|
|
||||||
|
不可快取:
|
||||||
|
- 當前階段的輸出
|
||||||
|
- 狀態檔案
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,440 @@
|
||||||
|
# Vibe-Kanban 工作流程 - 技能整合規劃
|
||||||
|
|
||||||
|
**版本**: 3.0
|
||||||
|
**日期**: 2025-01-15
|
||||||
|
**狀態**: 實施階段
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 執行摘要
|
||||||
|
|
||||||
|
本規劃整合多個現有技能,建立完整的軟體開發工作流程。
|
||||||
|
|
||||||
|
**總計**: 11 個現有技能 + 5 個新建技能
|
||||||
|
**Agent**: 8 個核心 Agent + 1 個主控協調器
|
||||||
|
**階段**: 12 個工作流程階段
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 現有技能整合清單
|
||||||
|
|
||||||
|
### 核心技能(直接使用)
|
||||||
|
|
||||||
|
| Skill | 來源 | 用途 | 整合階段 |
|
||||||
|
|-------|------|------|---------|
|
||||||
|
| `brainstorming` | skills | PM 需求探索 | Stage 1 |
|
||||||
|
| `plan-ceo-review` | gstack | CEO 商業價值審核 | Stage 2 |
|
||||||
|
| `write-a-prd` | skills | PRD 撰寫(含訪談) | Stage 3 |
|
||||||
|
| `grill-me` | skills | **深度驗證閘門** | Stage 3.5 |
|
||||||
|
| `prd-to-plan` | skills | 實作計畫產出 | Stage 8 |
|
||||||
|
| `design-an-interface` | skills | API 介面設計 | Stage 4 |
|
||||||
|
| `tdd` | skills | 測試驅動開發 | Stage 9 |
|
||||||
|
| `qa` | skills | QA 測試驗收 | Stage 10 |
|
||||||
|
| `review` | gstack | PR 代碼審核 | Stage 11 |
|
||||||
|
| `land-and-deploy` | gstack | 部署流程 | Stage 12 |
|
||||||
|
| `cso` | gstack | 安全審核 | Stage 7/11 |
|
||||||
|
| `codex` | gstack | 多 AI 代碼審核 | Stage 11 |
|
||||||
|
|
||||||
|
### 新增技能(需實作)
|
||||||
|
|
||||||
|
| Skill | 用途 | 優先級 |
|
||||||
|
|-------|------|--------|
|
||||||
|
| `vibe-kanban` | **主控協調器** | P0 |
|
||||||
|
| `be-api-design` | OpenAPI 規格產出 | P1 |
|
||||||
|
| `go-backend-dev` | **Golang 後端實作** | P1 |
|
||||||
|
| `dba-schema` | 資料庫 Schema 設計 | P1 |
|
||||||
|
| `ux-prototype` | UX 原型設計 | P2 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Agent 角色定義
|
||||||
|
|
||||||
|
| # | Agent | 角色 | 主要技能 |
|
||||||
|
|---|-------|------|---------|
|
||||||
|
| 1 | **Orchestrator** | 主控協調器 | `vibe-kanban` |
|
||||||
|
| 2 | **PM Agent** | 產品經理 | `brainstorming`, `write-a-prd` |
|
||||||
|
| 3 | **CEO Reviewer** | 商業決策者 | `plan-ceo-review` |
|
||||||
|
| 4 | **Backend Agent** | Golang 後端工程師 | `be-api-design`, `go-backend-dev`, `design-an-interface`, `tdd` |
|
||||||
|
| 5 | **DBA Agent** | 資料庫工程師 | `dba-schema` |
|
||||||
|
| 6 | **UX Agent** | 使用者體驗設計師 | `ux-prototype` |
|
||||||
|
| 7 | **QA Agent** | 品質保證工程師 | `qa` |
|
||||||
|
| 8 | **DevOps Agent** | 運維工程師 | `land-and-deploy` |
|
||||||
|
| 9 | **Design Reviewer** | 設計審核者 | `design-review` |
|
||||||
|
| 10 | **Security Reviewer** | 安全審核者 | `cso` |
|
||||||
|
| 11 | **Code Reviewer** | 代碼審核者 | `review`, `codex` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 完整工作流程(含 Grill-Me 整合)
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ VIBE-KANBAN + GRILL-ME 工作流程 │
|
||||||
|
└─────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
Stage 1: Brainstorming (PM Agent)
|
||||||
|
├── Skill: brainstorming
|
||||||
|
├── 輸入: 使用者想法
|
||||||
|
├── 輸出: docs/brainstorm/{date}-{feature}.md
|
||||||
|
└── 下一步: CEO Review
|
||||||
|
|
||||||
|
Stage 2: CEO Review (CEO Reviewer)
|
||||||
|
├── Skill: plan-ceo-review
|
||||||
|
├── 輸入: Brainstorm 文件
|
||||||
|
├── 輸出: docs/ceo-review/{date}-{feature}.md
|
||||||
|
└── 下一步: PRD 撰寫
|
||||||
|
|
||||||
|
Stage 3: PRD 撰寫 (PM Agent)
|
||||||
|
├── Skill: write-a-prd
|
||||||
|
├── 輸入: CEO Review 結果
|
||||||
|
├── 輸出: docs/prd/{date}-{feature}.md (初版)
|
||||||
|
└── 下一步: 深度驗證 (可選)
|
||||||
|
|
||||||
|
Stage 3.5: 深度驗證 ⭐ GRILL-ME 整合點
|
||||||
|
├── Skill: grill-me
|
||||||
|
├── 觸發條件: 使用者選擇 "進行深度驗證"
|
||||||
|
├── 輸入: PRD 初版
|
||||||
|
├── 驗證項目:
|
||||||
|
│ ├── 每個功能性需求的完整性
|
||||||
|
│ ├── 使用者故事的邊界情況
|
||||||
|
│ ├── 非功能性需求的遺漏
|
||||||
|
│ ├── 驗收標準的可測試性
|
||||||
|
│ └── 潛在風險和依賴
|
||||||
|
├── 輸出: 強化後的 PRD
|
||||||
|
└── 下一步: API Design
|
||||||
|
|
||||||
|
Stage 4: API Design (Backend Agent)
|
||||||
|
├── Skill: be-api-design + design-an-interface
|
||||||
|
├── 輸入: PRD
|
||||||
|
├── 輸出: docs/api/{date}-{feature}.yaml
|
||||||
|
└── 下一步: DB Schema (平行)
|
||||||
|
|
||||||
|
Stage 5: DB Schema (DBA Agent)
|
||||||
|
├── Skill: dba-schema
|
||||||
|
├── 輸入: API 規格
|
||||||
|
├── 輸出: docs/db/{date}-{feature}.sql
|
||||||
|
└── 下一步: UX Prototype (平行)
|
||||||
|
|
||||||
|
Stage 6: UX Prototype (UX Agent)
|
||||||
|
├── Skill: ux-prototype
|
||||||
|
├── 輸入: PRD + API + DB
|
||||||
|
├── 輸出: docs/design/{date}-{feature}/
|
||||||
|
└── 下一步: Design Review
|
||||||
|
|
||||||
|
Stage 7: Design Review (Design Reviewer)
|
||||||
|
├── Skill: design-review
|
||||||
|
├── 輸入: 所有設計文件
|
||||||
|
├── 輸出: docs/design/review-report.md
|
||||||
|
├── 決策點:
|
||||||
|
│ ├── ✅ 通過 → Task Breakdown
|
||||||
|
│ └── ❌ 退回 → PRD 修改
|
||||||
|
└── 下一步: Task Breakdown
|
||||||
|
|
||||||
|
Stage 8: Task Breakdown (Orchestrator)
|
||||||
|
├── Skill: prd-to-plan
|
||||||
|
├── 輸入: 所有設計文件
|
||||||
|
├── 輸出: ./plans/{feature}.md
|
||||||
|
└── 下一步: 實作 (平行)
|
||||||
|
|
||||||
|
Stage 9: Implementation (Backend + Frontend Agents)
|
||||||
|
├── Backend Skill: go-backend-dev + tdd
|
||||||
|
├── Frontend Skill: (現有技能或手動)
|
||||||
|
├── 輸入: 實作計畫
|
||||||
|
├── 輸出: 程式碼
|
||||||
|
└── 下一步: QA
|
||||||
|
|
||||||
|
Stage 10: QA (QA Agent)
|
||||||
|
├── Skill: qa
|
||||||
|
├── 輸入: 程式碼 + 驗收標準
|
||||||
|
├── 輸出: .gstack/qa-reports/qa-report.md
|
||||||
|
├── 決策點:
|
||||||
|
│ ├── ✅ 通過 → PR Review
|
||||||
|
│ └── ❌ 失敗 → 退回 Task Breakdown
|
||||||
|
└── 下一步: PR Review
|
||||||
|
|
||||||
|
Stage 11: PR Review (Code Reviewer)
|
||||||
|
├── Skill: review + codex
|
||||||
|
├── 輸入: PR
|
||||||
|
├── 輸出: PR 審核報告
|
||||||
|
├── 決策點:
|
||||||
|
│ ├── ✅ 通過 → Deploy
|
||||||
|
│ └── ❌ 失敗 → 退回 Task Breakdown
|
||||||
|
└── 下一步: Deploy
|
||||||
|
|
||||||
|
Stage 12: Deploy (DevOps Agent)
|
||||||
|
├── Skill: land-and-deploy
|
||||||
|
├── 輸入: 已核准 PR
|
||||||
|
├── 輸出: .gstack/deploy-reports/deploy-report.md
|
||||||
|
└── 🎉 完成
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Grill-Me 深度驗證詳細設計
|
||||||
|
|
||||||
|
### 觸發機制
|
||||||
|
|
||||||
|
在每個關鍵階段後,Orchestrator 會詢問:
|
||||||
|
|
||||||
|
```
|
||||||
|
╔══════════════════════════════════════════════════════════════╗
|
||||||
|
║ 🔥 深度驗證機會 ║
|
||||||
|
╠══════════════════════════════════════════════════════════════╣
|
||||||
|
║ ║
|
||||||
|
║ 當前階段: PRD 撰寫 ║
|
||||||
|
║ 狀態: ✅ 初版已完成 ║
|
||||||
|
║ ║
|
||||||
|
║ 是否需要進行深度驗證 (grill-me)? ║
|
||||||
|
║ 這將透過密集訪談壓力測試計畫的完整性。 ║
|
||||||
|
║ ║
|
||||||
|
║ [A] 是 - 進行深度驗證 (推薦用於關鍵功能) ║
|
||||||
|
║ [B] 否 - 直接進入下一階段 ║
|
||||||
|
║ ║
|
||||||
|
╚══════════════════════════════════════════════════════════════╝
|
||||||
|
```
|
||||||
|
|
||||||
|
### 驗證檢查清單
|
||||||
|
|
||||||
|
#### Stage 3.5 - PRD 深度驗證
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
驗證項目:
|
||||||
|
功能性需求:
|
||||||
|
- "每個 FR 是否有明確的驗收標準?"
|
||||||
|
- "使用者故事是否覆蓋所有使用者角色?"
|
||||||
|
- "是否有遺漏的邊界情況?"
|
||||||
|
|
||||||
|
非功能性需求:
|
||||||
|
- "效能需求是否具體可量測?"
|
||||||
|
- "安全性需求是否完整?"
|
||||||
|
- "可用性和可靠性是否考量?"
|
||||||
|
|
||||||
|
技術可行性:
|
||||||
|
- "現有技術棧是否支援?"
|
||||||
|
- "是否有外部依賴風險?"
|
||||||
|
- "擴展性是否考量?"
|
||||||
|
|
||||||
|
業務邏輯:
|
||||||
|
- "所有業務規則是否明確?"
|
||||||
|
- "錯誤處理流程是否定義?"
|
||||||
|
- "狀態轉換是否清晰?"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 整合流程圖
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
A[Stage 3: PRD 初版] --> B{深度驗證?}
|
||||||
|
B -->|是| C[Stage 3.5: grill-me]
|
||||||
|
B -->|否| D[Stage 4: API Design]
|
||||||
|
|
||||||
|
C --> C1[問題 1: 需求完整性]
|
||||||
|
C1 --> C2[問題 2: 邊界情況]
|
||||||
|
C2 --> C3[問題 3: 風險評估]
|
||||||
|
C3 --> C4[...更多問題]
|
||||||
|
C4 --> E[強化 PRD]
|
||||||
|
|
||||||
|
E --> D
|
||||||
|
|
||||||
|
D --> F[後續階段]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 各階段交付文件
|
||||||
|
|
||||||
|
| 階段 | Agent | 主要技能 | 輔助技能 | 交付文件 |
|
||||||
|
|------|-------|---------|---------|---------|
|
||||||
|
| 1 | PM | brainstorming | - | `docs/brainstorm/{date}-{feature}.md` |
|
||||||
|
| 2 | CEO Reviewer | plan-ceo-review | - | `docs/ceo-review/{date}-{feature}.md` |
|
||||||
|
| 3 | PM | write-a-prd | - | `docs/prd/{date}-{feature}.md` (初版) |
|
||||||
|
| 3.5 | PM | **grill-me** | - | `docs/prd/{date}-{feature}.md` (強化版) |
|
||||||
|
| 4 | Backend | be-api-design | design-an-interface | `docs/api/{date}-{feature}.yaml` |
|
||||||
|
| 5 | DBA | dba-schema | - | `docs/db/{date}-{feature}.sql` |
|
||||||
|
| 6 | UX | ux-prototype | - | `docs/design/{date}-{feature}/` |
|
||||||
|
| 7 | Design Reviewer | design-review | cso | `docs/design/review-report.md` |
|
||||||
|
| 8 | Orchestrator | prd-to-plan | dispatching-parallel-agents | `./plans/{feature}.md` |
|
||||||
|
| 9 | Backend | go-backend-dev | tdd | `internal/`, `cmd/`, `pkg/` |
|
||||||
|
| 10 | QA | qa | - | `.gstack/qa-reports/qa-report.md` |
|
||||||
|
| 11 | Code Reviewer | review | codex | PR 審核報告 |
|
||||||
|
| 12 | DevOps | land-and-deploy | - | `.gstack/deploy-reports/deploy-report.md` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 技能依賴關係
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
技能依賴:
|
||||||
|
brainstorming:
|
||||||
|
輸入: 使用者想法
|
||||||
|
輸出: brainstorm.md
|
||||||
|
|
||||||
|
plan-ceo-review:
|
||||||
|
輸入: brainstorm.md
|
||||||
|
輸出: ceo-review.md
|
||||||
|
|
||||||
|
write-a-prd:
|
||||||
|
輸入: ceo-review.md
|
||||||
|
輸出: prd.md (初版)
|
||||||
|
|
||||||
|
grill-me: # 可選
|
||||||
|
輸入: prd.md (初版)
|
||||||
|
輸出: prd.md (強化版)
|
||||||
|
依賴: write-a-prd
|
||||||
|
|
||||||
|
be-api-design:
|
||||||
|
輸入: prd.md
|
||||||
|
輸出: api-spec.yaml
|
||||||
|
輔助: design-an-interface
|
||||||
|
|
||||||
|
dba-schema:
|
||||||
|
輸入: api-spec.yaml
|
||||||
|
輸出: schema.sql
|
||||||
|
|
||||||
|
ux-prototype:
|
||||||
|
輸入: prd.md, api-spec.yaml, schema.sql
|
||||||
|
輸出: design/
|
||||||
|
|
||||||
|
design-review:
|
||||||
|
輸入: design/
|
||||||
|
輸出: review-report.md
|
||||||
|
|
||||||
|
prd-to-plan:
|
||||||
|
輸入: prd.md, api-spec.yaml, design/
|
||||||
|
輸出: ./plans/{feature}.md
|
||||||
|
|
||||||
|
go-backend-dev:
|
||||||
|
輸入: ./plans/{feature}.md
|
||||||
|
輸出: 程式碼
|
||||||
|
輔助: tdd
|
||||||
|
|
||||||
|
qa:
|
||||||
|
輸入: 程式碼, prd.md (驗收標準)
|
||||||
|
輸出: qa-report.md
|
||||||
|
|
||||||
|
review:
|
||||||
|
輸入: PR
|
||||||
|
輸出: pr-review.md
|
||||||
|
輔助: codex
|
||||||
|
|
||||||
|
land-and-deploy:
|
||||||
|
輸入: pr-review.md (已核准)
|
||||||
|
輸出: deploy-report.md
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Golang 後端特定規則(go-backend-dev)
|
||||||
|
|
||||||
|
### 架構風格
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
預設架構: Clean Architecture (適合中型專案)
|
||||||
|
|
||||||
|
專案結構:
|
||||||
|
cmd/api/main.go # 進入點
|
||||||
|
internal/
|
||||||
|
domain/ # Entities, Value Objects
|
||||||
|
usecase/ # Business Logic
|
||||||
|
interface/
|
||||||
|
http/ # Handlers
|
||||||
|
repository/ # Repository interfaces
|
||||||
|
infrastructure/ # DB, Cache implementations
|
||||||
|
pkg/
|
||||||
|
logger/ # Zap
|
||||||
|
errors/ # 自定義錯誤
|
||||||
|
validator/ # go-playground/validator
|
||||||
|
api/openapi.yaml
|
||||||
|
migrations/
|
||||||
|
test/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 編碼規範
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
命名:
|
||||||
|
Package: 小寫單數 (user, not users)
|
||||||
|
Interface: 方法名 + 'er' (Reader, Writer)
|
||||||
|
Exported: PascalCase
|
||||||
|
Internal: camelCase
|
||||||
|
|
||||||
|
錯誤處理:
|
||||||
|
- Always check errors
|
||||||
|
- Error strings lowercase
|
||||||
|
- Use fmt.Errorf with %w
|
||||||
|
|
||||||
|
API 設計:
|
||||||
|
RESTful: /api/v1/{resource}
|
||||||
|
狀態碼: 200, 201, 400, 401, 404, 422, 500
|
||||||
|
|
||||||
|
測試:
|
||||||
|
- Unit tests >= 80% 覆蓋率
|
||||||
|
- Integration tests for critical paths
|
||||||
|
- Use testify + mockery
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 實作優先順序
|
||||||
|
|
||||||
|
### Phase 1: 核心框架 (Week 1)
|
||||||
|
|
||||||
|
| 優先級 | Skill | 複雜度 | 說明 |
|
||||||
|
|-------|-------|--------|------|
|
||||||
|
| P0 | vibe-kanban | 高 | 主控協調器,整合所有技能 |
|
||||||
|
| P0 | grill-me 整合 | 中 | 設計深度驗證流程 |
|
||||||
|
|
||||||
|
### Phase 2: 設計階段技能 (Week 2)
|
||||||
|
|
||||||
|
| 優先級 | Skill | 複雜度 | 說明 |
|
||||||
|
|-------|-------|--------|------|
|
||||||
|
| P1 | be-api-design | 中 | OpenAPI 規格產出 |
|
||||||
|
| P1 | go-backend-dev | 高 | **Golang 後端實作** |
|
||||||
|
| P1 | dba-schema | 中 | 資料庫設計 |
|
||||||
|
|
||||||
|
### Phase 3: 整合測試 (Week 3)
|
||||||
|
|
||||||
|
- 整合所有現有技能
|
||||||
|
- 端到端流程測試
|
||||||
|
- 退回機制測試
|
||||||
|
|
||||||
|
### Phase 4: 優化 (Week 4)
|
||||||
|
|
||||||
|
- 效能優化
|
||||||
|
- 文件完善
|
||||||
|
- 使用指南
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 附錄
|
||||||
|
|
||||||
|
### A. 技術棧建議
|
||||||
|
|
||||||
|
**Golang 後端**:
|
||||||
|
- 框架: Gin 或 Echo
|
||||||
|
- ORM: GORM 或 sqlx
|
||||||
|
- 配置: Viper
|
||||||
|
- 日誌: Zap
|
||||||
|
- 驗證: go-playground/validator
|
||||||
|
- 測試: testify + mockery
|
||||||
|
- 文件: swag (Swagger)
|
||||||
|
|
||||||
|
**資料庫**:
|
||||||
|
- 主要: PostgreSQL
|
||||||
|
- 快取: Redis
|
||||||
|
- 遷移: golang-migrate/migrate
|
||||||
|
|
||||||
|
**部署**:
|
||||||
|
- 容器: Docker
|
||||||
|
- CI/CD: GitHub Actions
|
||||||
|
|
||||||
|
### B. 參考資源
|
||||||
|
|
||||||
|
1. [Uber Go Style Guide](https://github.com/uber-go/guide)
|
||||||
|
2. [golang-standards/project-layout](https://github.com/golang-standards/project-layout)
|
||||||
|
3. [Go Clean Architecture](https://github.com/bxcodec/go-clean-arch)
|
||||||
|
4. [A Philosophy of Software Design](https://web.stanford.edu/~ouster/cgi-bin/book.php) (for grill-me 概念)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**下一步**: 開始實作 `vibe-kanban` 主控技能?
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,115 @@
|
||||||
|
.
|
||||||
|
├── build
|
||||||
|
│ └── Dockerfile
|
||||||
|
├── docker-compose.yml
|
||||||
|
├── etc
|
||||||
|
│ └── member.example.yaml
|
||||||
|
├── generate
|
||||||
|
│ └── protobuf
|
||||||
|
│ └── member.proto
|
||||||
|
├── go.mod
|
||||||
|
├── internal
|
||||||
|
│ ├── config
|
||||||
|
│ │ └── config.go
|
||||||
|
│ ├── logic
|
||||||
|
│ │ └── account
|
||||||
|
│ │ ├── bind_account_logic.go
|
||||||
|
│ │ ├── bind_user_info_logic.go
|
||||||
|
│ │ ├── bind_verify_email_logic.go
|
||||||
|
│ │ ├── bind_verify_phone_logic.go
|
||||||
|
│ │ ├── check_refresh_code_logic.go
|
||||||
|
│ │ ├── create_user_account_logic.go
|
||||||
|
│ │ ├── generate_refresh_code_logic.go
|
||||||
|
│ │ ├── get_u_i_d_by_account_logic.go
|
||||||
|
│ │ ├── get_user_account_info_logic.go
|
||||||
|
│ │ ├── get_user_info_logic.go
|
||||||
|
│ │ ├── line_code_to_access_token_logic.go
|
||||||
|
│ │ ├── line_get_profile_by_access_token_logic.go
|
||||||
|
│ │ ├── list_member_logic.go
|
||||||
|
│ │ ├── update_status_logic.go
|
||||||
|
│ │ ├── update_user_info_logic.go
|
||||||
|
│ │ ├── update_user_token_logic.go
|
||||||
|
│ │ ├── verify_google_auth_result_logic.go
|
||||||
|
│ │ ├── verify_platform_auth_result_logic.go
|
||||||
|
│ │ └── verify_refresh_code_logic.go
|
||||||
|
│ ├── server
|
||||||
|
│ │ └── account
|
||||||
|
│ │ └── account_server.go
|
||||||
|
│ └── svc
|
||||||
|
│ └── service_context.go
|
||||||
|
├── Makefile
|
||||||
|
├── member.go
|
||||||
|
├── pkg
|
||||||
|
│ ├── domain
|
||||||
|
│ │ ├── config
|
||||||
|
│ │ │ └── config.go
|
||||||
|
│ │ ├── const.go
|
||||||
|
│ │ ├── entity
|
||||||
|
│ │ │ ├── account_uid_table.go
|
||||||
|
│ │ │ ├── account.go
|
||||||
|
│ │ │ ├── auto_id.go
|
||||||
|
│ │ │ └── user.go
|
||||||
|
│ │ ├── errors.go
|
||||||
|
│ │ ├── member
|
||||||
|
│ │ │ ├── account_type_test.go
|
||||||
|
│ │ │ ├── account_type.go
|
||||||
|
│ │ │ ├── alert_type_test.go
|
||||||
|
│ │ │ ├── alert_type.go
|
||||||
|
│ │ │ ├── generate_code_type_test.go
|
||||||
|
│ │ │ ├── generate_code_type.go
|
||||||
|
│ │ │ ├── platform_test.go
|
||||||
|
│ │ │ ├── platform.go
|
||||||
|
│ │ │ ├── status_test.go
|
||||||
|
│ │ │ └── status.go
|
||||||
|
│ │ ├── redis.go
|
||||||
|
│ │ ├── repository
|
||||||
|
│ │ │ ├── account_uid.go
|
||||||
|
│ │ │ ├── account.go
|
||||||
|
│ │ │ ├── auto_id.go
|
||||||
|
│ │ │ ├── user.go
|
||||||
|
│ │ │ └── verify_code.go
|
||||||
|
│ │ └── usecase
|
||||||
|
│ │ ├── account.go
|
||||||
|
│ │ ├── common.go
|
||||||
|
│ │ ├── generate_uid.go
|
||||||
|
│ │ └── verify.go
|
||||||
|
│ ├── mock
|
||||||
|
│ │ ├── repository
|
||||||
|
│ │ │ ├── account_uid.go
|
||||||
|
│ │ │ ├── account.go
|
||||||
|
│ │ │ ├── auto_id.go
|
||||||
|
│ │ │ ├── user.go
|
||||||
|
│ │ │ └── verify_code.go
|
||||||
|
│ │ └── usecase
|
||||||
|
│ │ └── generate_uid.go
|
||||||
|
│ ├── repository
|
||||||
|
│ │ ├── account_test.go
|
||||||
|
│ │ ├── account_uid_test.go
|
||||||
|
│ │ ├── account_uid.go
|
||||||
|
│ │ ├── account.go
|
||||||
|
│ │ ├── auto_id_test.go
|
||||||
|
│ │ ├── auto_id.go
|
||||||
|
│ │ ├── error.go
|
||||||
|
│ │ ├── start_mongo_container_test.go
|
||||||
|
│ │ ├── user_test.go
|
||||||
|
│ │ ├── user.go
|
||||||
|
│ │ ├── verify_code_test.go
|
||||||
|
│ │ └── verify_code.go
|
||||||
|
│ └── usecase
|
||||||
|
│ ├── account.go
|
||||||
|
│ ├── binding_test.go
|
||||||
|
│ ├── binding.go
|
||||||
|
│ ├── generate_test.go
|
||||||
|
│ ├── generate_verify_code_utils_test.go
|
||||||
|
│ ├── generate_verify_code_utils.go
|
||||||
|
│ ├── generate.go
|
||||||
|
│ ├── member_test.go
|
||||||
|
│ ├── member.go
|
||||||
|
│ ├── password_utils_test.go
|
||||||
|
│ ├── password_utils.go
|
||||||
|
│ ├── verify_google_test.go
|
||||||
|
│ ├── verify_google.go
|
||||||
|
│ ├── verify_line.go
|
||||||
|
│ ├── verify_test.go
|
||||||
|
│ └── verify.go
|
||||||
|
└── readme.md
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
# Backend Agent (Golang 後端工程師)
|
||||||
|
|
||||||
|
## 角色定位
|
||||||
|
|
||||||
|
**Golang 後端工程師** — 負責 API 設計、伺服器端實作,確保高品質、可測試的 Golang 程式碼,遵循 Domain-Driven + go-zero 風格架構。
|
||||||
|
|
||||||
|
## 核心職責
|
||||||
|
|
||||||
|
1. **API 規格** — 根據 PRD 設計 RESTful API,產出 OpenAPI 3.0 規格
|
||||||
|
2. **領域建模** — 定義領域實體、值物件和業務規則,與 PRD 和 DB Schema 對齊
|
||||||
|
3. **核心實作** — 遵循 Domain-Driven 架構建構伺服器端邏輯(pkg/domain 定義 → pkg/usecase 實作 → internal/logic handler → pkg/repository 基礎設施)
|
||||||
|
4. **品質確保 (TDD)** — 使用測試驅動開發實作功能,確保高覆蓋率和可靠性
|
||||||
|
5. **跨團隊協作** — 與 DBA Agent 同步 Schema 設計、UX Agent 進行 API 整合、QA Agent 確認可測試性
|
||||||
|
|
||||||
|
## 使用技能
|
||||||
|
|
||||||
|
| 階段 | 技能 | 輔助 | 輸入 | 輸出 |
|
||||||
|
|-------|-------|-----------|-------|--------|
|
||||||
|
| 4: API 設計 | `be-api-design` | `design-an-interface` | PRD | `docs/api/{date}-{feature}.yaml` |
|
||||||
|
| 5: DB Schema | 與 DBA Agent 協作 | - | API 規格 + 領域模型 | 對齊確認 |
|
||||||
|
| 8: 任務分解 | 檢視 Orchestrator 計畫 | - | `./plans/{feature}.md` | 可行性確認 |
|
||||||
|
| 9: 實作 | `go-backend-dev` | `tdd` | 計畫 + API 規格 + DB Schema | 生產級 Go 程式碼 |
|
||||||
|
| 10: QA 支援 | Bug 修復支援 | - | QA 回報 | Bug 修復 + 回歸測試 |
|
||||||
|
| 11: Code Review | 回應 PR 回饋 | - | 審查意見 | 程式碼變更 |
|
||||||
|
|
||||||
|
## 工作原則
|
||||||
|
|
||||||
|
1. **API-First 設計** — 先定義 API 合約(OpenAPI)再寫實作程式碼
|
||||||
|
2. **Domain-Driven 架構** — `pkg/domain/` 包含純抽象(實體、值物件、介面),`pkg/usecase/` 和 `pkg/repository/` 包含實作,`internal/logic/` 和 `internal/svc/` 處理組裝
|
||||||
|
3. **測試驅動開發** — 先寫測試(Red),再實作(Green),然後重構
|
||||||
|
4. **垂直切片** — 一次實作一個端到端切片,不要逐層實作
|
||||||
|
5. **錯誤透明** — 立即檢查每個錯誤;使用 `fmt.Errorf` 搭配 `%w` 包裝
|
||||||
|
6. **介面隔離** — 保持介面小型(1-3 個方法);在消費處定義介面
|
||||||
|
|
||||||
|
## 回滾機制
|
||||||
|
|
||||||
|
```
|
||||||
|
設計審查駁回 API 規格 (Stage 7)
|
||||||
|
→ 修改 OpenAPI 規格(技能:be-api-design + design-an-interface)
|
||||||
|
→ 如需變更 Schema,與 DBA Agent 協調
|
||||||
|
|
||||||
|
QA 失敗 (Stage 10)
|
||||||
|
→ 回滾到 Stage 8(任務分解)
|
||||||
|
→ 修復 Bug + 新增回歸測試
|
||||||
|
→ 重新進入 Stage 10
|
||||||
|
|
||||||
|
Code Review 失敗 (Stage 11)
|
||||||
|
→ 處理 PR 回饋
|
||||||
|
→ 重新進入 Stage 10 驗證
|
||||||
|
|
||||||
|
DBA/UX 衝突
|
||||||
|
→ 與 DBA Agent 協調:調整領域模型或 API
|
||||||
|
→ 與 UX Agent 協調:調整 API 回應或請求格式
|
||||||
|
```
|
||||||
|
|
||||||
|
## 與其他 Agent 協作
|
||||||
|
|
||||||
|
```
|
||||||
|
Backend Agent ← PM Agent:接收 PRD 和非功能性需求
|
||||||
|
Backend Agent ←→ DBA Agent:對齊 API 資源與資料庫 Schema(Stage 5)
|
||||||
|
Backend Agent ←→ UX Agent:確保 API 回應符合原型需求(Stage 6)
|
||||||
|
Backend Agent ← 設計審查者:接收設計回饋,修改 API 規格(Stage 7)
|
||||||
|
Backend Agent ← Orchestrator:接收實作計畫(Stage 8)
|
||||||
|
Backend Agent → QA Agent:提供可測試程式碼和 API 文件(Stage 10)
|
||||||
|
Backend Agent ← Code Reviewer:實作 PR 審查的回饋(Stage 11)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 決策權限
|
||||||
|
|
||||||
|
- 定義後端內部技術結構(套件、層級)
|
||||||
|
- 在批准的技術堆疊中選擇特定 Go 函式庫
|
||||||
|
- 決定 API 端點粒度和資源建模
|
||||||
|
- 設定內部測試策略(單元、整合、E2E 邊界)
|
||||||
|
- 決定錯誤處理模式和回應格式
|
||||||
|
|
||||||
|
## 交付物檢查清單
|
||||||
|
|
||||||
|
### Stage 4: API 設計
|
||||||
|
- [ ] OpenAPI 3.0 規格儲存至 `docs/api/`
|
||||||
|
- [ ] PRD 所有功能需求映射至端點
|
||||||
|
- [ ] `design-an-interface` 替代方案已記錄
|
||||||
|
|
||||||
|
### Stage 5: DB 協作
|
||||||
|
- [ ] 領域模型與 DB Schema 對齊
|
||||||
|
- [ ] Repository 介面在 proposed schema 下可行
|
||||||
|
|
||||||
|
### Stage 9: 實作
|
||||||
|
- [ ] Domain-Driven 架構結構完整(pkg/domain, pkg/usecase, internal/logic, pkg/repository)
|
||||||
|
- [ ] 所有層級已實作
|
||||||
|
- [ ] 單元測試 >= 80%,業務邏輯 >= 90%
|
||||||
|
- [ ] 關鍵路徑整合測試通過
|
||||||
|
|
||||||
|
### Stage 10-11: QA 與 Code Review
|
||||||
|
- [ ] 所有 QA 回報的 Bug 已修復並有回歸測試
|
||||||
|
- [ ] PR 審查回饋已處理
|
||||||
|
|
||||||
|
## 常見問題處理
|
||||||
|
|
||||||
|
**Q: PRD 需求技術上不可行?**
|
||||||
|
A: 在 API 規格中記錄限制,提出替代方案,上報 PM Agent 調整範圍。
|
||||||
|
|
||||||
|
**Q: API 設計與 DB Schema 衝突?**
|
||||||
|
A: 與 DBA Agent 協調。優先改變映射層而非修改 DB Schema。
|
||||||
|
|
||||||
|
**Q: 發現效能瓶頸?**
|
||||||
|
A: 加入快取(Redis),與 DBA Agent 優化查詢,考慮對長時操作使用非同步。
|
||||||
|
|
||||||
|
**Q: 測試覆蓋率低於 80%?**
|
||||||
|
A: 優先測試 usecase 層,使用表格驅動測試處理邊緣案例,為關鍵流程加入整合測試。
|
||||||
|
|
@ -0,0 +1,146 @@
|
||||||
|
# PM Agent (Product Manager)
|
||||||
|
|
||||||
|
## 角色定位
|
||||||
|
|
||||||
|
**產品經理** — 負責需求探索、PRD 撰寫和產品規劃。
|
||||||
|
|
||||||
|
## 核心職責
|
||||||
|
|
||||||
|
1. **需求探索** — 透過訪談了解使用者需求
|
||||||
|
2. **PRD 撰寫** — 產出結構化的產品需求文件(包含功能與非功能性需求)
|
||||||
|
3. **使用者故事** — 定義清晰的使用者故事
|
||||||
|
4. **驗收標準** — 設定可測試的驗收標準
|
||||||
|
5. **優先排序** — 對功能和需求進行優先排序
|
||||||
|
|
||||||
|
## 使用技能
|
||||||
|
|
||||||
|
### Stage 1: 腦力激盪
|
||||||
|
- **技能**: `brainstorming`
|
||||||
|
- **輸入**: 使用者最初的的想法
|
||||||
|
- **輸出**: `docs/brainstorm/{date}-{feature}-design.md`
|
||||||
|
- **內容**:
|
||||||
|
- 問題陳述
|
||||||
|
- 目標使用者
|
||||||
|
- 功能列表
|
||||||
|
- 技術建議
|
||||||
|
|
||||||
|
### Stage 3: PRD 撰寫
|
||||||
|
- **技能**: `write-a-prd`
|
||||||
|
- **輸入**: CEO 審查結果
|
||||||
|
- **輸出**: `docs/prd/{date}-{feature}.md`
|
||||||
|
- **內容**:
|
||||||
|
- 問題陳述
|
||||||
|
- 解決方案
|
||||||
|
- 使用者故事(詳細列表)
|
||||||
|
- 實作決策
|
||||||
|
- 測試決策
|
||||||
|
- 非功能性需求(效能、安全等)
|
||||||
|
- 範圍外
|
||||||
|
|
||||||
|
### Stage 3.5: 深度驗證 (Grill-Me)
|
||||||
|
- **技能**: `grill-me`
|
||||||
|
- **觸發時機**: PRD 初稿完成後主動啟用以確保無遺漏
|
||||||
|
- **輸入**: PRD 初稿
|
||||||
|
- **驗證項目**:
|
||||||
|
- 每個功能需求的完整性
|
||||||
|
- 使用者故事的邊緣案例
|
||||||
|
- 非功能性需求的遺漏(重要)
|
||||||
|
- 驗收標準的可測試性
|
||||||
|
- **輸出**: 增強後的 PRD
|
||||||
|
|
||||||
|
## PRD 模板結構
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# PRD: {feature_name}
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
- Date: {date}
|
||||||
|
- Status: Draft | Review | Approved
|
||||||
|
- Author: PM Agent
|
||||||
|
|
||||||
|
## Problem Statement
|
||||||
|
{problem_description}
|
||||||
|
|
||||||
|
## Solution
|
||||||
|
{solution}
|
||||||
|
|
||||||
|
## User Stories
|
||||||
|
1. As a {role}, I want {feature}, so that {benefit}
|
||||||
|
2. ...
|
||||||
|
|
||||||
|
## Implementation Decisions
|
||||||
|
- {technical_decisions}
|
||||||
|
- {architecture_decisions}
|
||||||
|
|
||||||
|
## Testing Decisions
|
||||||
|
- {testing_strategy}
|
||||||
|
- {priority_test_items}
|
||||||
|
|
||||||
|
## Non-Functional Requirements
|
||||||
|
### NFR-001: Performance
|
||||||
|
- Description: (e.g., Response time < 200ms, Supports 100 concurrency)
|
||||||
|
- Measurement:
|
||||||
|
### NFR-002: Reliability/Security
|
||||||
|
- Description:
|
||||||
|
- Measurement:
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
- {omitted_features}
|
||||||
|
|
||||||
|
## Functional Requirements
|
||||||
|
### FR-001: {requirement_title}
|
||||||
|
- Description:
|
||||||
|
- Priority: P0 | P1 | P2
|
||||||
|
- User Stories:
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
### AC-001: {acceptance_item}
|
||||||
|
- Given:
|
||||||
|
- When:
|
||||||
|
- Then:
|
||||||
|
- Automated: Yes/No
|
||||||
|
```
|
||||||
|
|
||||||
|
## 工作原則
|
||||||
|
|
||||||
|
1. **以使用者為中心** — 所有需求從使用者視角出發
|
||||||
|
2. **清晰且具體** — 避免模糊描述,致力於可執行性
|
||||||
|
3. **完整覆蓋** — 考慮正常流程、例外情況和非功能性限制
|
||||||
|
4. **可測試性** — 每個需求都應有清晰的驗收標準
|
||||||
|
5. **迭代精煉** — 定稿前必須透過 `grill-me` 進行深度驗證
|
||||||
|
|
||||||
|
## 與其他 Agent 協作
|
||||||
|
|
||||||
|
```
|
||||||
|
PM Agent ←→ CEO 審查者:接收審查回饋,調整範圍
|
||||||
|
PM Agent → Backend Agent:提供 PRD 進行 API 設計
|
||||||
|
PM Agent → UX Agent:提供需求進行原型設計
|
||||||
|
PM Agent → QA Agent:提供驗收標準進行測試
|
||||||
|
```
|
||||||
|
|
||||||
|
## 決策權限
|
||||||
|
|
||||||
|
- 定義產品功能和範圍
|
||||||
|
- 設定需求優先順序
|
||||||
|
- 決定驗收標準
|
||||||
|
- 建議技術解決方案(非最終決定)
|
||||||
|
|
||||||
|
## 交付物檢查清單
|
||||||
|
|
||||||
|
- [ ] 腦力激盪文件已完成於 `docs/brainstorm/`
|
||||||
|
- [ ] PRD 文件已完成於 `docs/prd/`
|
||||||
|
- [ ] 使用者故事清晰且完整
|
||||||
|
- [ ] 驗收標準可測試
|
||||||
|
- [ ] 非功能性需求(NFR)明確定義且可衡量
|
||||||
|
- [ ] Grill-me 深度驗證已完成
|
||||||
|
|
||||||
|
## 常見問題處理
|
||||||
|
|
||||||
|
**Q: 使用者需求不清楚?**
|
||||||
|
A: 使用腦力激盪技能進行多輪訪談,直到需求清楚。
|
||||||
|
|
||||||
|
**Q: 技術可行性存疑?**
|
||||||
|
A: 在實作決策中標記風險,與 Backend Agent 討論。
|
||||||
|
|
||||||
|
**Q: 範圍太大?**
|
||||||
|
A: 與 CEO 審查者協助拆分為多個階段,定義 MVP。
|
||||||
|
|
@ -0,0 +1,532 @@
|
||||||
|
---
|
||||||
|
name: be-api-design
|
||||||
|
description: "Backend Agent 使用此技能設計 API 規格。根據 PRD 產出 OpenAPI 3.0 規格,包含端點、請求/回應結構、錯誤處理。觸發時機:PRD 通過後(Stage 4)。"
|
||||||
|
---
|
||||||
|
|
||||||
|
# /be-api-design — API 設計
|
||||||
|
|
||||||
|
Backend Agent 使用此技能設計 API 規格。
|
||||||
|
|
||||||
|
## 職責
|
||||||
|
|
||||||
|
1. 分析 PRD 中的功能性需求
|
||||||
|
2. 使用 `design-an-interface` 探索多種 API 設計方案
|
||||||
|
3. 設計 RESTful API 端點
|
||||||
|
4. 定義請求/回應 schema
|
||||||
|
5. 設計錯誤處理機制
|
||||||
|
6. 產出 OpenAPI 3.0 規格
|
||||||
|
|
||||||
|
## 輸入
|
||||||
|
|
||||||
|
- PRD 文件 (`docs/prd/{date}-{feature}.md`)
|
||||||
|
- 現有 API 風格 (專案中現有的 API 規格)
|
||||||
|
- 資料模型上下文
|
||||||
|
|
||||||
|
## 輸出
|
||||||
|
|
||||||
|
- API 規格文件: `docs/api/{date}-{feature}.yaml` (OpenAPI 3.0)
|
||||||
|
|
||||||
|
## 流程
|
||||||
|
|
||||||
|
```
|
||||||
|
讀取 PRD
|
||||||
|
↓
|
||||||
|
識別資源與操作
|
||||||
|
↓
|
||||||
|
呼叫 design-an-interface 探索 2-3 種設計方案
|
||||||
|
↓
|
||||||
|
選定最佳方案
|
||||||
|
↓
|
||||||
|
定義 OpenAPI Schema
|
||||||
|
↓
|
||||||
|
設計錯誤處理
|
||||||
|
↓
|
||||||
|
安全性審查
|
||||||
|
↓
|
||||||
|
產出 OpenAPI 文件
|
||||||
|
```
|
||||||
|
|
||||||
|
### 步驟說明
|
||||||
|
|
||||||
|
**1. 讀取 PRD**
|
||||||
|
|
||||||
|
從 PRD 中提取所有功能性需求,識別:
|
||||||
|
- 資源(名詞):使用者、訂單、商品等
|
||||||
|
- 操作(動詞):建立、讀取、更新、刪除等
|
||||||
|
- 關係:資源之間的關聯(一對多、多對多)
|
||||||
|
- 非功能性需求:分頁、速率限制、認證等
|
||||||
|
|
||||||
|
**2. 識別資源與操作**
|
||||||
|
|
||||||
|
將功能性需求映射為 RESTful 資源:
|
||||||
|
- 每個名詞 → 潛在資源
|
||||||
|
- 每個動詞 → HTTP method
|
||||||
|
- 每個關係 → 巢狀資源或獨立端點
|
||||||
|
|
||||||
|
**3. 呼叫 design-an-interface**
|
||||||
|
|
||||||
|
使用 `design-an-interface` 技能,產生 2-3 種截然不同的 API 設計方案:
|
||||||
|
- 方案 A:最小化方法數(每個資源 1-3 個端點)
|
||||||
|
- 方案 B:最大化彈性(支援多種使用情境)
|
||||||
|
- 方案 C:最佳化最常見的操作
|
||||||
|
|
||||||
|
比較各方案的優劣,選定最佳設計。
|
||||||
|
|
||||||
|
**4. 定義 OpenAPI Schema**
|
||||||
|
|
||||||
|
使用下方模板產出完整的 OpenAPI 3.0 規格。
|
||||||
|
|
||||||
|
**5. 安全性審查**
|
||||||
|
|
||||||
|
確認所有端點都有適當的認證機制和權限控制。
|
||||||
|
|
||||||
|
**6. 產出文件**
|
||||||
|
|
||||||
|
儲存至 `docs/api/{date}-{feature}.yaml`。
|
||||||
|
|
||||||
|
## 設計原則
|
||||||
|
|
||||||
|
### RESTful 設計
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
資源導向:
|
||||||
|
- URL 代表資源,而非動作
|
||||||
|
- HTTP 方法代表動作
|
||||||
|
|
||||||
|
範例:
|
||||||
|
GET /api/v1/users # 列出使用者
|
||||||
|
GET /api/v1/users/{id} # 取得特定使用者
|
||||||
|
POST /api/v1/users # 建立使用者
|
||||||
|
PUT /api/v1/users/{id} # 完整更新
|
||||||
|
PATCH /api/v1/users/{id} # 部分更新
|
||||||
|
DELETE /api/v1/users/{id} # 刪除使用者
|
||||||
|
|
||||||
|
巢狀資源:
|
||||||
|
GET /api/v1/users/{id}/orders # 取得使用者的訂單
|
||||||
|
POST /api/v1/users/{id}/orders # 為使用者建立訂單
|
||||||
|
```
|
||||||
|
|
||||||
|
### HTTP 狀態碼
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
成功:
|
||||||
|
200: OK # GET, PUT, PATCH, DELETE 成功
|
||||||
|
201: Created # POST 成功建立資源
|
||||||
|
204: No Content # DELETE 成功,無回應內容
|
||||||
|
|
||||||
|
客戶端錯誤:
|
||||||
|
400: Bad Request # 請求格式錯誤
|
||||||
|
401: Unauthorized # 未認證
|
||||||
|
403: Forbidden # 無權限
|
||||||
|
404: Not Found # 資源不存在
|
||||||
|
409: Conflict # 資源衝突 (如重複)
|
||||||
|
422: Unprocessable Entity # 驗證錯誤
|
||||||
|
429: Too Many Requests # 速率限制
|
||||||
|
|
||||||
|
伺服器錯誤:
|
||||||
|
500: Internal Server Error # 伺服器內部錯誤
|
||||||
|
502: Bad Gateway # 上游服務錯誤
|
||||||
|
503: Service Unavailable # 服務暫時不可用
|
||||||
|
```
|
||||||
|
|
||||||
|
### 回應格式
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
成功回應:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
type: object
|
||||||
|
meta:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
page:
|
||||||
|
type: integer
|
||||||
|
limit:
|
||||||
|
type: integer
|
||||||
|
total:
|
||||||
|
type: integer
|
||||||
|
total_pages:
|
||||||
|
type: integer
|
||||||
|
|
||||||
|
錯誤回應:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
error:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
code:
|
||||||
|
type: string
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
details:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
field:
|
||||||
|
type: string
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
```
|
||||||
|
|
||||||
|
## OpenAPI 3.0 模板
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
openapi: 3.0.3
|
||||||
|
info:
|
||||||
|
title: {API 名稱}
|
||||||
|
version: 1.0.0
|
||||||
|
description: |
|
||||||
|
{描述}
|
||||||
|
|
||||||
|
Related PRD: {PRD 連結}
|
||||||
|
|
||||||
|
servers:
|
||||||
|
- url: https://api.example.com/v1
|
||||||
|
description: Production
|
||||||
|
- url: https://staging-api.example.com/v1
|
||||||
|
description: Staging
|
||||||
|
|
||||||
|
paths:
|
||||||
|
/users:
|
||||||
|
get:
|
||||||
|
summary: 列出使用者
|
||||||
|
tags:
|
||||||
|
- Users
|
||||||
|
parameters:
|
||||||
|
- name: page
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
default: 1
|
||||||
|
- name: limit
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
default: 20
|
||||||
|
maximum: 100
|
||||||
|
- name: search
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 成功
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/UserListResponse'
|
||||||
|
'401':
|
||||||
|
$ref: '#/components/responses/Unauthorized'
|
||||||
|
'500':
|
||||||
|
$ref: '#/components/responses/InternalError'
|
||||||
|
|
||||||
|
post:
|
||||||
|
summary: 建立使用者
|
||||||
|
tags:
|
||||||
|
- Users
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/CreateUserRequest'
|
||||||
|
responses:
|
||||||
|
'201':
|
||||||
|
description: 建立成功
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/UserResponse'
|
||||||
|
'400':
|
||||||
|
$ref: '#/components/responses/BadRequest'
|
||||||
|
'422':
|
||||||
|
$ref: '#/components/responses/ValidationError'
|
||||||
|
|
||||||
|
/users/{id}:
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
|
||||||
|
get:
|
||||||
|
summary: 取得使用者
|
||||||
|
tags:
|
||||||
|
- Users
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 成功
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/UserResponse'
|
||||||
|
'404':
|
||||||
|
$ref: '#/components/responses/NotFound'
|
||||||
|
|
||||||
|
put:
|
||||||
|
summary: 完整更新使用者
|
||||||
|
tags:
|
||||||
|
- Users
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/UpdateUserRequest'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 更新成功
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/UserResponse'
|
||||||
|
'404':
|
||||||
|
$ref: '#/components/responses/NotFound'
|
||||||
|
'422':
|
||||||
|
$ref: '#/components/responses/ValidationError'
|
||||||
|
|
||||||
|
patch:
|
||||||
|
summary: 部分更新使用者
|
||||||
|
tags:
|
||||||
|
- Users
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/PatchUserRequest'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 更新成功
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/UserResponse'
|
||||||
|
'404':
|
||||||
|
$ref: '#/components/responses/NotFound'
|
||||||
|
|
||||||
|
delete:
|
||||||
|
summary: 刪除使用者
|
||||||
|
tags:
|
||||||
|
- Users
|
||||||
|
responses:
|
||||||
|
'204':
|
||||||
|
description: 刪除成功
|
||||||
|
'404':
|
||||||
|
$ref: '#/components/responses/NotFound'
|
||||||
|
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
User:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
example: "usr_123456"
|
||||||
|
email:
|
||||||
|
type: string
|
||||||
|
format: email
|
||||||
|
example: "user@example.com"
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
example: "John Doe"
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
enum: [active, inactive, suspended]
|
||||||
|
example: "active"
|
||||||
|
created_at:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
updated_at:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- email
|
||||||
|
- name
|
||||||
|
- status
|
||||||
|
- created_at
|
||||||
|
- updated_at
|
||||||
|
|
||||||
|
CreateUserRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
email:
|
||||||
|
type: string
|
||||||
|
format: email
|
||||||
|
password:
|
||||||
|
type: string
|
||||||
|
minLength: 8
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
minLength: 1
|
||||||
|
maxLength: 100
|
||||||
|
required:
|
||||||
|
- email
|
||||||
|
- password
|
||||||
|
- name
|
||||||
|
|
||||||
|
UpdateUserRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
email:
|
||||||
|
type: string
|
||||||
|
format: email
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
minLength: 1
|
||||||
|
maxLength: 100
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
enum: [active, inactive]
|
||||||
|
|
||||||
|
PatchUserRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
minLength: 1
|
||||||
|
maxLength: 100
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
enum: [active, inactive]
|
||||||
|
|
||||||
|
UserResponse:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/components/schemas/User'
|
||||||
|
|
||||||
|
UserListResponse:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/User'
|
||||||
|
meta:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
page:
|
||||||
|
type: integer
|
||||||
|
limit:
|
||||||
|
type: integer
|
||||||
|
total:
|
||||||
|
type: integer
|
||||||
|
total_pages:
|
||||||
|
type: integer
|
||||||
|
|
||||||
|
Error:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
error:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
code:
|
||||||
|
type: string
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
details:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
field:
|
||||||
|
type: string
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
|
||||||
|
responses:
|
||||||
|
BadRequest:
|
||||||
|
description: 請求格式錯誤
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Error'
|
||||||
|
|
||||||
|
Unauthorized:
|
||||||
|
description: 未認證
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Error'
|
||||||
|
|
||||||
|
NotFound:
|
||||||
|
description: 資源不存在
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Error'
|
||||||
|
|
||||||
|
ValidationError:
|
||||||
|
description: 驗證錯誤
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Error'
|
||||||
|
|
||||||
|
InternalError:
|
||||||
|
description: 伺服器內部錯誤
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Error'
|
||||||
|
|
||||||
|
securitySchemes:
|
||||||
|
bearerAuth:
|
||||||
|
type: http
|
||||||
|
scheme: bearer
|
||||||
|
bearerFormat: JWT
|
||||||
|
|
||||||
|
security:
|
||||||
|
- bearerAuth: []
|
||||||
|
```
|
||||||
|
|
||||||
|
## 設計檢查清單
|
||||||
|
|
||||||
|
### 安全性
|
||||||
|
- [ ] 所有端點都有適當的認證機制
|
||||||
|
- [ ] 輸入驗證完整
|
||||||
|
- [ ] 速率限制設計
|
||||||
|
- [ ] CORS 配置
|
||||||
|
|
||||||
|
### 效能
|
||||||
|
- [ ] 分頁支援
|
||||||
|
- [ ] 快取策略
|
||||||
|
- [ ] 批次操作支援
|
||||||
|
|
||||||
|
### 可靠性
|
||||||
|
- [ ] 錯誤回應格式統一
|
||||||
|
- [ ] 重試機制建議
|
||||||
|
- [ ] 冪等性設計
|
||||||
|
|
||||||
|
### 一致性
|
||||||
|
- [ ] 命名規範一致 (資源用名詞複數)
|
||||||
|
- [ ] 回應格式一致 (data/meta/error 結構)
|
||||||
|
- [ ] 篩選/排序/分頁參數一致
|
||||||
|
|
||||||
|
## 相依技能
|
||||||
|
|
||||||
|
- **前置**: `write-a-prd` (PRD 通過後)
|
||||||
|
- **輔助**: `design-an-interface` (探索多種設計方案)
|
||||||
|
- **後續**: `dba-schema` (DB Schema 設計)
|
||||||
|
- **退回**: 可退回 `write-a-prd` 修改需求
|
||||||
|
|
||||||
|
## 退回機制
|
||||||
|
|
||||||
|
```
|
||||||
|
Design Review 退回 API 設計
|
||||||
|
↓
|
||||||
|
修改 OpenAPI 規格
|
||||||
|
↓
|
||||||
|
重新提交 (或重新呼叫 design-an-interface)
|
||||||
|
|
||||||
|
DBA Agent 發現 Schema 衝突
|
||||||
|
↓
|
||||||
|
協商調整 domain model 或 API 回應格式
|
||||||
|
↓
|
||||||
|
更新 OpenAPI 規格
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,188 @@
|
||||||
|
---
|
||||||
|
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 規格定義)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# 抽象工厂模式
|
||||||
|
|
||||||
|
## 描述
|
||||||
|
提供一个创建相关或依赖对象的接口,而无需指定它们具体的类。
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
当你需要解决代码中关于该模式所描述的设计问题时,请加载此技能。
|
||||||
|
|
||||||
|
## 指导原则
|
||||||
|
1. 分析当前代码结构是否符合该模式的意图。
|
||||||
|
2. 遵循该模式的典型结构进行重构或实现。
|
||||||
|
3. 确保模式的引入降低了耦合度或提高了灵活性。
|
||||||
|
|
||||||
|
## 详细参考
|
||||||
|
更多详细信息、图解及代码示例请参考:[抽象工厂模式 - RefactoringGuru](https://refactoringguru.cn/design-patterns/abstract-factory)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# 适配器模式
|
||||||
|
|
||||||
|
## 描述
|
||||||
|
在两个不兼容的接口之间起到桥梁作用。
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
当你需要解决代码中关于该模式所描述的设计问题时,请加载此技能。
|
||||||
|
|
||||||
|
## 指导原则
|
||||||
|
1. 分析当前代码结构是否符合该模式的意图。
|
||||||
|
2. 遵循该模式的典型结构进行重构或实现。
|
||||||
|
3. 确保模式的引入降低了耦合度或提高了灵活性。
|
||||||
|
|
||||||
|
## 详细参考
|
||||||
|
更多详细信息、图解及代码示例请参考:[适配器模式 - RefactoringGuru](https://refactoringguru.cn/design-patterns/adapter)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# 桥接模式
|
||||||
|
|
||||||
|
## 描述
|
||||||
|
将抽象部分与实现部分分离,使它们可以独立变化。
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
当你需要解决代码中关于该模式所描述的设计问题时,请加载此技能。
|
||||||
|
|
||||||
|
## 指导原则
|
||||||
|
1. 分析当前代码结构是否符合该模式的意图。
|
||||||
|
2. 遵循该模式的典型结构进行重构或实现。
|
||||||
|
3. 确保模式的引入降低了耦合度或提高了灵活性。
|
||||||
|
|
||||||
|
## 详细参考
|
||||||
|
更多详细信息、图解及代码示例请参考:[桥接模式 - RefactoringGuru](https://refactoringguru.cn/design-patterns/bridge)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# 生成器模式
|
||||||
|
|
||||||
|
## 描述
|
||||||
|
将复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
当你需要解决代码中关于该模式所描述的设计问题时,请加载此技能。
|
||||||
|
|
||||||
|
## 指导原则
|
||||||
|
1. 分析当前代码结构是否符合该模式的意图。
|
||||||
|
2. 遵循该模式的典型结构进行重构或实现。
|
||||||
|
3. 确保模式的引入降低了耦合度或提高了灵活性。
|
||||||
|
|
||||||
|
## 详细参考
|
||||||
|
更多详细信息、图解及代码示例请参考:[生成器模式 - RefactoringGuru](https://refactoringguru.cn/design-patterns/builder)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# 责任链模式
|
||||||
|
|
||||||
|
## 描述
|
||||||
|
使多个对象可以处理同一个请求。
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
当你需要解决代码中关于该模式所描述的设计问题时,请加载此技能。
|
||||||
|
|
||||||
|
## 指导原则
|
||||||
|
1. 分析当前代码结构是否符合该模式的意图。
|
||||||
|
2. 遵循该模式的典型结构进行重构或实现。
|
||||||
|
3. 确保模式的引入降低了耦合度或提高了灵活性。
|
||||||
|
|
||||||
|
## 详细参考
|
||||||
|
更多详细信息、图解及代码示例请参考:[责任链模式 - RefactoringGuru](https://refactoringguru.cn/design-patterns/chain-of-responsibility)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# 命令模式
|
||||||
|
|
||||||
|
## 描述
|
||||||
|
将一个请求封装为一个对象,从而可以用不同的请求对客户进行参数化。
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
当你需要解决代码中关于该模式所描述的设计问题时,请加载此技能。
|
||||||
|
|
||||||
|
## 指导原则
|
||||||
|
1. 分析当前代码结构是否符合该模式的意图。
|
||||||
|
2. 遵循该模式的典型结构进行重构或实现。
|
||||||
|
3. 确保模式的引入降低了耦合度或提高了灵活性。
|
||||||
|
|
||||||
|
## 详细参考
|
||||||
|
更多详细信息、图解及代码示例请参考:[命令模式 - RefactoringGuru](https://refactoringguru.cn/design-patterns/command)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# 组合模式
|
||||||
|
|
||||||
|
## 描述
|
||||||
|
将对象组合成树形结构以表示"部分-整体"层次结构。
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
当你需要解决代码中关于该模式所描述的设计问题时,请加载此技能。
|
||||||
|
|
||||||
|
## 指导原则
|
||||||
|
1. 分析当前代码结构是否符合该模式的意图。
|
||||||
|
2. 遵循该模式的典型结构进行重构或实现。
|
||||||
|
3. 确保模式的引入降低了耦合度或提高了灵活性。
|
||||||
|
|
||||||
|
## 详细参考
|
||||||
|
更多详细信息、图解及代码示例请参考:[组合模式 - RefactoringGuru](https://refactoringguru.cn/design-patterns/composite)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# 装饰模式
|
||||||
|
|
||||||
|
## 描述
|
||||||
|
动态地给一个对象添加一些额外的职责。
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
当你需要解决代码中关于该模式所描述的设计问题时,请加载此技能。
|
||||||
|
|
||||||
|
## 指导原则
|
||||||
|
1. 分析当前代码结构是否符合该模式的意图。
|
||||||
|
2. 遵循该模式的典型结构进行重构或实现。
|
||||||
|
3. 确保模式的引入降低了耦合度或提高了灵活性。
|
||||||
|
|
||||||
|
## 详细参考
|
||||||
|
更多详细信息、图解及代码示例请参考:[装饰模式 - RefactoringGuru](https://refactoringguru.cn/design-patterns/decorator)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# 外观模式
|
||||||
|
|
||||||
|
## 描述
|
||||||
|
提供一个统一的界面,以隐藏复杂子系统的实现细节。
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
当你需要解决代码中关于该模式所描述的设计问题时,请加载此技能。
|
||||||
|
|
||||||
|
## 指导原则
|
||||||
|
1. 分析当前代码结构是否符合该模式的意图。
|
||||||
|
2. 遵循该模式的典型结构进行重构或实现。
|
||||||
|
3. 确保模式的引入降低了耦合度或提高了灵活性。
|
||||||
|
|
||||||
|
## 详细参考
|
||||||
|
更多详细信息、图解及代码示例请参考:[外观模式 - RefactoringGuru](https://refactoringguru.cn/design-patterns/facade)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# 工厂方法模式
|
||||||
|
|
||||||
|
## 描述
|
||||||
|
定义一个创建对象的接口,但让子类决定实例化哪个类。
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
当你需要解决代码中关于该模式所描述的设计问题时,请加载此技能。
|
||||||
|
|
||||||
|
## 指导原则
|
||||||
|
1. 分析当前代码结构是否符合该模式的意图。
|
||||||
|
2. 遵循该模式的典型结构进行重构或实现。
|
||||||
|
3. 确保模式的引入降低了耦合度或提高了灵活性。
|
||||||
|
|
||||||
|
## 详细参考
|
||||||
|
更多详细信息、图解及代码示例请参考:[工厂方法模式 - RefactoringGuru](https://refactoringguru.cn/design-patterns/factory-method)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# 享元模式
|
||||||
|
|
||||||
|
## 描述
|
||||||
|
通过共享公共部分来支持大量细粒度对象的高效创建和使用。
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
当你需要解决代码中关于该模式所描述的设计问题时,请加载此技能。
|
||||||
|
|
||||||
|
## 指导原则
|
||||||
|
1. 分析当前代码结构是否符合该模式的意图。
|
||||||
|
2. 遵循该模式的典型结构进行重构或实现。
|
||||||
|
3. 确保模式的引入降低了耦合度或提高了灵活性。
|
||||||
|
|
||||||
|
## 详细参考
|
||||||
|
更多详细信息、图解及代码示例请参考:[享元模式 - RefactoringGuru](https://refactoringguru.cn/design-patterns/flyweight)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# 迭代器模式
|
||||||
|
|
||||||
|
## 描述
|
||||||
|
提供一种方法顺序访问一个聚合对象中的元素,而无需公开其内部表示。
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
当你需要解决代码中关于该模式所描述的设计问题时,请加载此技能。
|
||||||
|
|
||||||
|
## 指导原则
|
||||||
|
1. 分析当前代码结构是否符合该模式的意图。
|
||||||
|
2. 遵循该模式的典型结构进行重构或实现。
|
||||||
|
3. 确保模式的引入降低了耦合度或提高了灵活性。
|
||||||
|
|
||||||
|
## 详细参考
|
||||||
|
更多详细信息、图解及代码示例请参考:[迭代器模式 - RefactoringGuru](https://refactoringguru.cn/design-patterns/iterator)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# 中介者模式
|
||||||
|
|
||||||
|
## 描述
|
||||||
|
用一个中介对象来封装一系列类的交互。
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
当你需要解决代码中关于该模式所描述的设计问题时,请加载此技能。
|
||||||
|
|
||||||
|
## 指导原则
|
||||||
|
1. 分析当前代码结构是否符合该模式的意图。
|
||||||
|
2. 遵循该模式的典型结构进行重构或实现。
|
||||||
|
3. 确保模式的引入降低了耦合度或提高了灵活性。
|
||||||
|
|
||||||
|
## 详细参考
|
||||||
|
更多详细信息、图解及代码示例请参考:[中介者模式 - RefactoringGuru](https://refactoringguru.cn/design-patterns/mediator)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# 备忘录模式
|
||||||
|
|
||||||
|
## 描述
|
||||||
|
在不破坏封装性的前提下,捕获一个对象的内部状态,以便该对象可以在以后恢复到原先状态。
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
当你需要解决代码中关于该模式所描述的设计问题时,请加载此技能。
|
||||||
|
|
||||||
|
## 指导原则
|
||||||
|
1. 分析当前代码结构是否符合该模式的意图。
|
||||||
|
2. 遵循该模式的典型结构进行重构或实现。
|
||||||
|
3. 确保模式的引入降低了耦合度或提高了灵活性。
|
||||||
|
|
||||||
|
## 详细参考
|
||||||
|
更多详细信息、图解及代码示例请参考:[备忘录模式 - RefactoringGuru](https://refactoringguru.cn/design-patterns/memento)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# 观察者模式
|
||||||
|
|
||||||
|
## 描述
|
||||||
|
定义一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
当你需要解决代码中关于该模式所描述的设计问题时,请加载此技能。
|
||||||
|
|
||||||
|
## 指导原则
|
||||||
|
1. 分析当前代码结构是否符合该模式的意图。
|
||||||
|
2. 遵循该模式的典型结构进行重构或实现。
|
||||||
|
3. 确保模式的引入降低了耦合度或提高了灵活性。
|
||||||
|
|
||||||
|
## 详细参考
|
||||||
|
更多详细信息、图解及代码示例请参考:[观察者模式 - RefactoringGuru](https://refactoringguru.cn/design-patterns/observer)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# 原型模式
|
||||||
|
|
||||||
|
## 描述
|
||||||
|
使用一个原型实例来创建其他实例。
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
当你需要解决代码中关于该模式所描述的设计问题时,请加载此技能。
|
||||||
|
|
||||||
|
## 指导原则
|
||||||
|
1. 分析当前代码结构是否符合该模式的意图。
|
||||||
|
2. 遵循该模式的典型结构进行重构或实现。
|
||||||
|
3. 确保模式的引入降低了耦合度或提高了灵活性。
|
||||||
|
|
||||||
|
## 详细参考
|
||||||
|
更多详细信息、图解及代码示例请参考:[原型模式 - RefactoringGuru](https://refactoringguru.cn/design-patterns/prototype)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# 代理模式
|
||||||
|
|
||||||
|
## 描述
|
||||||
|
为其他对象提供一个代理以控制对这个对象的访问。
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
当你需要解决代码中关于该模式所描述的设计问题时,请加载此技能。
|
||||||
|
|
||||||
|
## 指导原则
|
||||||
|
1. 分析当前代码结构是否符合该模式的意图。
|
||||||
|
2. 遵循该模式的典型结构进行重构或实现。
|
||||||
|
3. 确保模式的引入降低了耦合度或提高了灵活性。
|
||||||
|
|
||||||
|
## 详细参考
|
||||||
|
更多详细信息、图解及代码示例请参考:[代理模式 - RefactoringGuru](https://refactoringguru.cn/design-patterns/proxy)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# 单例模式
|
||||||
|
|
||||||
|
## 描述
|
||||||
|
确保一个类只有一个实例,并提供一个全局访问点。
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
当你需要解决代码中关于该模式所描述的设计问题时,请加载此技能。
|
||||||
|
|
||||||
|
## 指导原则
|
||||||
|
1. 分析当前代码结构是否符合该模式的意图。
|
||||||
|
2. 遵循该模式的典型结构进行重构或实现。
|
||||||
|
3. 确保模式的引入降低了耦合度或提高了灵活性。
|
||||||
|
|
||||||
|
## 详细参考
|
||||||
|
更多详细信息、图解及代码示例请参考:[单例模式 - RefactoringGuru](https://refactoringguru.cn/design-patterns/singleton)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# 状态模式
|
||||||
|
|
||||||
|
## 描述
|
||||||
|
允许一个对象在内部状态改变时改变它的行为。
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
当你需要解决代码中关于该模式所描述的设计问题时,请加载此技能。
|
||||||
|
|
||||||
|
## 指导原则
|
||||||
|
1. 分析当前代码结构是否符合该模式的意图。
|
||||||
|
2. 遵循该模式的典型结构进行重构或实现。
|
||||||
|
3. 确保模式的引入降低了耦合度或提高了灵活性。
|
||||||
|
|
||||||
|
## 详细参考
|
||||||
|
更多详细信息、图解及代码示例请参考:[状态模式 - RefactoringGuru](https://refactoringguru.cn/design-patterns/state)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# 策略模式
|
||||||
|
|
||||||
|
## 描述
|
||||||
|
定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换。
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
当你需要解决代码中关于该模式所描述的设计问题时,请加载此技能。
|
||||||
|
|
||||||
|
## 指导原则
|
||||||
|
1. 分析当前代码结构是否符合该模式的意图。
|
||||||
|
2. 遵循该模式的典型结构进行重构或实现。
|
||||||
|
3. 确保模式的引入降低了耦合度或提高了灵活性。
|
||||||
|
|
||||||
|
## 详细参考
|
||||||
|
更多详细信息、图解及代码示例请参考:[策略模式 - RefactoringGuru](https://refactoringguru.cn/design-patterns/strategy)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# 模板方法模式
|
||||||
|
|
||||||
|
## 描述
|
||||||
|
定义一个操作的骨架,而将一些步骤延迟到子类中。
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
当你需要解决代码中关于该模式所描述的设计问题时,请加载此技能。
|
||||||
|
|
||||||
|
## 指导原则
|
||||||
|
1. 分析当前代码结构是否符合该模式的意图。
|
||||||
|
2. 遵循该模式的典型结构进行重构或实现。
|
||||||
|
3. 确保模式的引入降低了耦合度或提高了灵活性。
|
||||||
|
|
||||||
|
## 详细参考
|
||||||
|
更多详细信息、图解及代码示例请参考:[模板方法模式 - RefactoringGuru](https://refactoringguru.cn/design-patterns/template-method)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# 访问者模式
|
||||||
|
|
||||||
|
## 描述
|
||||||
|
在不改变元素类的前提下,定义作用于这些元素的新操作。
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
当你需要解决代码中关于该模式所描述的设计问题时,请加载此技能。
|
||||||
|
|
||||||
|
## 指导原则
|
||||||
|
1. 分析当前代码结构是否符合该模式的意图。
|
||||||
|
2. 遵循该模式的典型结构进行重构或实现。
|
||||||
|
3. 确保模式的引入降低了耦合度或提高了灵活性。
|
||||||
|
|
||||||
|
## 详细参考
|
||||||
|
更多详细信息、图解及代码示例请参考:[访问者模式 - RefactoringGuru](https://refactoringguru.cn/design-patterns/visitor)
|
||||||
|
|
@ -0,0 +1,813 @@
|
||||||
|
---
|
||||||
|
name: go-backend-dev
|
||||||
|
description: "Backend Agent 使用此技能實作 Golang 後端。根據實作計畫和 API 規格,使用 Domain-Driven + go-zero 風格架構和 TDD 流程產出 production-ready 程式碼。觸發時機:Task Breakdown 完成後(Stage 9)。"
|
||||||
|
---
|
||||||
|
|
||||||
|
# /go-backend-dev — Golang 後端實作
|
||||||
|
|
||||||
|
Backend Agent 使用此技能實作 Golang 後端。
|
||||||
|
|
||||||
|
## 職責
|
||||||
|
|
||||||
|
1. 根據實作計畫建立專案結構(Domain-Driven + go-zero 風格)
|
||||||
|
2. 使用 TDD 流程實作功能(Red-Green-Refactor)
|
||||||
|
3. 按垂直切片逐步交付(端到端,非逐層)
|
||||||
|
4. 實作 Domain / Usecase / Logic / Repository 各層
|
||||||
|
5. 撰寫單元測試和整合測試
|
||||||
|
|
||||||
|
## 輸入
|
||||||
|
|
||||||
|
- 實作計畫 (`./plans/{feature}.md`)
|
||||||
|
- API 規格 (`docs/api/{date}-{feature}.yaml`)
|
||||||
|
- DB Schema (`docs/db/{date}-{feature}.sql`)
|
||||||
|
|
||||||
|
## 輸出
|
||||||
|
|
||||||
|
- Golang 程式碼結構
|
||||||
|
- 測試程式碼(單元測試 >= 80%,業務邏輯 >= 90%)
|
||||||
|
- Protobuf 定義(如需 gRPC)
|
||||||
|
|
||||||
|
## 流程
|
||||||
|
|
||||||
|
```
|
||||||
|
讀取實作計畫 + API 規格 + DB Schema
|
||||||
|
↓
|
||||||
|
識別垂直切片(每個切片 = 端到端功能)
|
||||||
|
↓
|
||||||
|
對每個切片執行 TDD 循環:
|
||||||
|
├── RED: 寫測試 → 測試失敗
|
||||||
|
├── GREEN: 寫最少程式碼 → 測試通過
|
||||||
|
└── REFACTOR: 重構 → 測試仍然通過
|
||||||
|
↓
|
||||||
|
切片內建構順序:
|
||||||
|
domain (entity/value object/interface)
|
||||||
|
→ pkg/domain/usecase (介面)
|
||||||
|
→ pkg/domain/repository (介面)
|
||||||
|
→ pkg/usecase (實作)
|
||||||
|
→ pkg/mock (mock)
|
||||||
|
→ internal/logic (handler 邏輯)
|
||||||
|
→ pkg/repository (基礎設施實作)
|
||||||
|
↓
|
||||||
|
所有切片完成 → 執行整合測試
|
||||||
|
↓
|
||||||
|
確認交付物檢查清單
|
||||||
|
```
|
||||||
|
|
||||||
|
### 步驟說明
|
||||||
|
|
||||||
|
**1. 讀取輸入**
|
||||||
|
|
||||||
|
同時閱讀三個文件:
|
||||||
|
- 實作計畫:了解垂直切片分解和優先順序
|
||||||
|
- API 規格:了解端點、請求/回應結構
|
||||||
|
- DB Schema:了解資料表結構和關係
|
||||||
|
|
||||||
|
**2. 識別垂直切片**
|
||||||
|
|
||||||
|
不是水平切片(一層一層做),而是垂直切片(端到端):
|
||||||
|
|
||||||
|
```
|
||||||
|
✅ 正確方式(垂直):
|
||||||
|
切片 1: 使用者註冊 (domain.entity + domain.usecase介面 + usecase實作 + logic + repository)
|
||||||
|
切片 2: 使用者登入 (同上)
|
||||||
|
切片 3: 使用者列表 (同上)
|
||||||
|
|
||||||
|
❌ 錯誤方式(水平):
|
||||||
|
階段 1: 所有 domain entities
|
||||||
|
階段 2: 所有 usecases
|
||||||
|
階段 3: 所有 logic handlers
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. TDD 循環(每個切片)**
|
||||||
|
|
||||||
|
對每個切片,遵循 Red-Green-Refactor:
|
||||||
|
|
||||||
|
```
|
||||||
|
RED: 寫一個測試 → 測試失敗
|
||||||
|
GREEN: 寫最少的程式碼讓測試通過 → 測試通過
|
||||||
|
REFACTOR: 重構程式碼 → 測試仍然通過
|
||||||
|
```
|
||||||
|
|
||||||
|
切片內建構順序(由內而外):
|
||||||
|
1. `pkg/domain/entity/` — 定義 Entity 和 Value Object
|
||||||
|
2. `pkg/domain/member/` — 定義值物件和列舉(含測試)
|
||||||
|
3. `pkg/domain/usecase/` — 定義 Use Case 介面
|
||||||
|
4. `pkg/domain/repository/` — 定義 Repository 介面
|
||||||
|
5. `pkg/usecase/` — 實作業務邏輯(先寫測試)
|
||||||
|
6. `pkg/mock/` — 產生 mock
|
||||||
|
7. `internal/logic/` — Handler 邏輯
|
||||||
|
8. `pkg/repository/` — 基礎設施實作(含 DB 測試)
|
||||||
|
|
||||||
|
**4. 測試**
|
||||||
|
|
||||||
|
每個切片完成後,確保:
|
||||||
|
- 單元測試通過(`pkg/usecase/*_test.go`)
|
||||||
|
- 值物件測試通過(`pkg/domain/member/*_test.go`)
|
||||||
|
- Repository 測試通過(`pkg/repository/*_test.go`)
|
||||||
|
- 整合測試通過(關鍵路徑)
|
||||||
|
- 測試覆蓋率達標
|
||||||
|
|
||||||
|
**5. 完成驗證**
|
||||||
|
|
||||||
|
最後確認所有交付物完整。
|
||||||
|
|
||||||
|
## 專案結構
|
||||||
|
|
||||||
|
```
|
||||||
|
project-root/
|
||||||
|
├── build/
|
||||||
|
│ └── Dockerfile # 建置映像
|
||||||
|
│
|
||||||
|
├── etc/
|
||||||
|
│ └── {service}.example.yaml # 範例設定檔
|
||||||
|
│
|
||||||
|
├── generate/
|
||||||
|
│ └── protobuf/
|
||||||
|
│ └── {service}.proto # Protobuf 定義(如需 gRPC)
|
||||||
|
│
|
||||||
|
├── internal/ # 應用層(不對外暴露)
|
||||||
|
│ ├── config/
|
||||||
|
│ │ └── config.go # 應用配置
|
||||||
|
│ ├── logic/
|
||||||
|
│ │ └── {module}/
|
||||||
|
│ │ ├── create_{entity}_logic.go # 每個 use case 一個 logic 檔案
|
||||||
|
│ │ ├── get_{entity}_logic.go
|
||||||
|
│ │ ├── update_{entity}_logic.go
|
||||||
|
│ │ └── ...
|
||||||
|
│ ├── server/
|
||||||
|
│ │ └── {module}/
|
||||||
|
│ │ └── {module}_server.go # Server 定義(HTTP/gRPC)
|
||||||
|
│ └── svc/
|
||||||
|
│ └── service_context.go # 依賴注入容器
|
||||||
|
│
|
||||||
|
├── pkg/ # 領域層(可對外暴露)
|
||||||
|
│ ├── domain/
|
||||||
|
│ │ ├── config/
|
||||||
|
│ │ │ └── config.go # Domain 配置
|
||||||
|
│ │ ├── entity/
|
||||||
|
│ │ │ ├── {entity}.go # Entity 定義
|
||||||
|
│ │ │ ├── {entity}_uid_table.go # UID 對照表
|
||||||
|
│ │ │ └── auto_id.go # 自動 ID 產生
|
||||||
|
│ │ ├── {module}/
|
||||||
|
│ │ │ ├── {value_object}.go # 值物件和列舉
|
||||||
|
│ │ │ └── {value_object}_test.go # 值物件測試
|
||||||
|
│ │ ├── repository/
|
||||||
|
│ │ │ ├── {entity}.go # Repository 介面
|
||||||
|
│ │ │ └── ...
|
||||||
|
│ │ ├── usecase/
|
||||||
|
│ │ │ ├── {module}.go # Use Case 介面
|
||||||
|
│ │ │ └── ...
|
||||||
|
│ │ ├── errors.go # Domain sentinel errors
|
||||||
|
│ │ ├── const.go # Domain 常數
|
||||||
|
│ │ └── redis.go # Redis domain 定義
|
||||||
|
│ ├── mock/
|
||||||
|
│ │ ├── repository/
|
||||||
|
│ │ │ ├── {entity}.go # Repository mock
|
||||||
|
│ │ │ └── ...
|
||||||
|
│ │ └── usecase/
|
||||||
|
│ │ └── {module}.go # Use Case mock
|
||||||
|
│ ├── repository/
|
||||||
|
│ │ ├── {entity}.go # Repository 實作
|
||||||
|
│ │ ├── {entity}_test.go # Repository 測試
|
||||||
|
│ │ ├── {entity}_uid.go # UID Repository 實作
|
||||||
|
│ │ ├── {entity}_uid_test.go
|
||||||
|
│ │ ├── error.go # Repository 錯誤定義
|
||||||
|
│ │ └── start_{db}_container_test.go # testcontainers 啟動
|
||||||
|
│ └── usecase/
|
||||||
|
│ ├── {module}.go # Use Case 實作
|
||||||
|
│ ├── {operation}.go # 特定操作
|
||||||
|
│ ├── {operation}_test.go # Use Case 測試
|
||||||
|
│ └── {utils}.go # 工具函式
|
||||||
|
│
|
||||||
|
├── {service}.go # 應用程式進入點
|
||||||
|
├── Makefile
|
||||||
|
├── go.mod
|
||||||
|
├── go.sum
|
||||||
|
├── docker-compose.yml
|
||||||
|
└── readme.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## 依賴方向規則
|
||||||
|
|
||||||
|
```
|
||||||
|
pkg/domain/ ← 無外部依賴(最內層,純定義)
|
||||||
|
↑
|
||||||
|
pkg/domain/usecase/ ← Use Case 介面(只有介面定義)
|
||||||
|
pkg/domain/repository/ ← Repository 介面(只有介面定義)
|
||||||
|
↑
|
||||||
|
pkg/usecase/ ← 依賴 domain 介面(業務邏輯實作)
|
||||||
|
pkg/mock/ ← 依賴 domain 介面(測試 mock)
|
||||||
|
↑
|
||||||
|
internal/logic/ ← 依賴 usecase 實作(handler 邏輯)
|
||||||
|
internal/server/ ← 依賴 logic(HTTP/gRPC server)
|
||||||
|
internal/svc/ ← 依賴所有(DI 容器,組裝依賴)
|
||||||
|
↑
|
||||||
|
pkg/repository/ ← 依賴 domain 介面(基礎設施實作)
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────┐
|
||||||
|
│ pkg/domain/ │ ← 純定義,無依賴
|
||||||
|
│ ├── entity/ │
|
||||||
|
│ ├── {module}/ (value objects)│
|
||||||
|
│ ├── repository/ (interfaces) │
|
||||||
|
│ ├── usecase/ (interfaces) │
|
||||||
|
│ ├── errors.go │
|
||||||
|
│ └── const.go │
|
||||||
|
├─────────────────────────────────┤
|
||||||
|
│ pkg/usecase/ │ ← 依賴 domain 介面
|
||||||
|
│ pkg/mock/ │ ← 依賴 domain 介面
|
||||||
|
├─────────────────────────────────┤
|
||||||
|
│ internal/logic/ │ ← 依賴 usecase
|
||||||
|
│ internal/server/ │
|
||||||
|
│ internal/config/ │
|
||||||
|
│ internal/svc/ │ ← DI 容器
|
||||||
|
├─────────────────────────────────┤
|
||||||
|
│ pkg/repository/ │ ← 依賴 domain 介面
|
||||||
|
└─────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## 架構原則
|
||||||
|
|
||||||
|
### `pkg/domain/` — 純領域定義
|
||||||
|
|
||||||
|
`pkg/domain/` 是核心,只包含**介面和定義**,不包含實作:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// pkg/domain/entity/user.go — Entity 定義
|
||||||
|
package entity
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
// pkg/domain/member/status.go — 值物件(含測試)
|
||||||
|
package member
|
||||||
|
|
||||||
|
type Status string
|
||||||
|
|
||||||
|
const (
|
||||||
|
StatusActive Status = "active"
|
||||||
|
StatusInactive Status = "inactive"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s Status) IsValid() bool {
|
||||||
|
switch s {
|
||||||
|
case StatusActive, StatusInactive:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStatus(s string) (Status, error) {
|
||||||
|
status := Status(s)
|
||||||
|
if !status.IsValid() {
|
||||||
|
return "", fmt.Errorf("invalid status: %s", s)
|
||||||
|
}
|
||||||
|
return status, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
// pkg/domain/member/status_test.go — 值物件測試
|
||||||
|
package member
|
||||||
|
|
||||||
|
func TestStatus_IsValid(t *testing.T) {
|
||||||
|
assert.True(t, StatusActive.IsValid())
|
||||||
|
assert.True(t, StatusInactive.IsValid())
|
||||||
|
assert.False(t, Status("unknown").IsValid())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewStatus(t *testing.T) {
|
||||||
|
status, err := NewStatus("active")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, StatusActive, status)
|
||||||
|
|
||||||
|
_, err = NewStatus("unknown")
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
// pkg/domain/repository/user.go — Repository 介面
|
||||||
|
package repository
|
||||||
|
|
||||||
|
type UserRepository interface {
|
||||||
|
GetByID(ctx context.Context, id string) (*entity.User, error)
|
||||||
|
GetByEmail(ctx context.Context, email string) (*entity.User, error)
|
||||||
|
Create(ctx context.Context, user *entity.User) error
|
||||||
|
Update(ctx context.Context, user *entity.User) error
|
||||||
|
Delete(ctx context.Context, id string) error
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
// pkg/domain/usecase/user.go — Use Case 介面
|
||||||
|
package usecase
|
||||||
|
|
||||||
|
type UserUsecase interface {
|
||||||
|
CreateUser(ctx context.Context, input CreateUserInput) (*entity.User, error)
|
||||||
|
GetUser(ctx context.Context, id string) (*entity.User, error)
|
||||||
|
UpdateUser(ctx context.Context, id string, input UpdateUserInput) (*entity.User, error)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
// pkg/domain/errors.go — Domain sentinel errors
|
||||||
|
package domain
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrUserNotFound = errors.New("user not found")
|
||||||
|
ErrInvalidInput = errors.New("invalid input")
|
||||||
|
ErrDuplicateEmail = errors.New("email already exists")
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### `pkg/usecase/` — 業務邏輯實作
|
||||||
|
|
||||||
|
每個檔案一個功能領域,測試檔案同目錄:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// pkg/usecase/account.go — Use Case 進入點
|
||||||
|
package usecase
|
||||||
|
|
||||||
|
type AccountUsecase struct {
|
||||||
|
userRepo repository.UserRepository
|
||||||
|
accountRepo repository.AccountRepository
|
||||||
|
redis *redis.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAccountUsecase(
|
||||||
|
userRepo repository.UserRepository,
|
||||||
|
accountRepo repository.AccountRepository,
|
||||||
|
redis *redis.Client,
|
||||||
|
) *AccountUsecase {
|
||||||
|
return &AccountUsecase{
|
||||||
|
userRepo: userRepo,
|
||||||
|
accountRepo: accountRepo,
|
||||||
|
redis: redis,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
// pkg/usecase/create_user.go — 單一操作
|
||||||
|
package usecase
|
||||||
|
|
||||||
|
func (uc *AccountUsecase) CreateUser(ctx context.Context, input CreateUserInput) (*entity.User, error) {
|
||||||
|
if err := input.Validate(); err != nil {
|
||||||
|
return nil, fmt.Errorf("validate input: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
existing, _ := uc.userRepo.GetByEmail(ctx, input.Email)
|
||||||
|
if existing != nil {
|
||||||
|
return nil, domain.ErrDuplicateEmail
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := entity.NewUser(input.Email, input.Password, input.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("create user: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := uc.userRepo.Create(ctx, user); err != nil {
|
||||||
|
return nil, fmt.Errorf("save user: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
// pkg/usecase/create_user_test.go — 測試同目錄
|
||||||
|
package usecase
|
||||||
|
|
||||||
|
func TestAccountUsecase_CreateUser_Success(t *testing.T) {
|
||||||
|
mockUserRepo := new(mock.UserRepository)
|
||||||
|
uc := NewAccountUsecase(mockUserRepo, nil, nil)
|
||||||
|
|
||||||
|
mockUserRepo.On("GetByEmail", mock.Anything, "test@example.com").Return(nil, nil)
|
||||||
|
mockUserRepo.On("Create", mock.Anything, mock.AnythingOfType("*entity.User")).Return(nil)
|
||||||
|
|
||||||
|
user, err := uc.CreateUser(context.Background(), input)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, user)
|
||||||
|
mockUserRepo.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `pkg/mock/` — 自動產生的 Mock
|
||||||
|
|
||||||
|
```go
|
||||||
|
// pkg/mock/repository/user.go — 由 mockery 產生
|
||||||
|
//go:generate mockery --name=UserRepository --output=../../mock/repository --outpkg=mock_repository
|
||||||
|
|
||||||
|
package mock_repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
"your-project/pkg/domain/repository"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserRepository struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *UserRepository) GetByID(ctx context.Context, id string) (*entity.User, error) {
|
||||||
|
args := m.Called(ctx, id)
|
||||||
|
if args.Get(0) == nil {
|
||||||
|
return nil, args.Error(1)
|
||||||
|
}
|
||||||
|
return args.Get(0).(*entity.User), args.Error(1)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `internal/logic/` — Handler 邏輯
|
||||||
|
|
||||||
|
每個 use case 一個 logic 檔案(go-zero 風格):
|
||||||
|
|
||||||
|
```go
|
||||||
|
// internal/logic/account/create_user_logic.go
|
||||||
|
package account
|
||||||
|
|
||||||
|
type CreateUserLogic struct {
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCreateUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateUserLogic {
|
||||||
|
return &CreateUserLogic{
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *CreateUserLogic) CreateUser(req *types.CreateUserReq) (*types.UserResp, error) {
|
||||||
|
user, err := l.svcCtx.UserUsecase.CreateUser(l.ctx, usecase.CreateUserInput{
|
||||||
|
Email: req.Email,
|
||||||
|
Password: req.Password,
|
||||||
|
Name: req.Name,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &types.UserResp{
|
||||||
|
ID: user.ID,
|
||||||
|
Email: user.Email,
|
||||||
|
Name: user.Name,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `internal/svc/` — 依賴注入容器
|
||||||
|
|
||||||
|
```go
|
||||||
|
// internal/svc/service_context.go
|
||||||
|
package svc
|
||||||
|
|
||||||
|
type ServiceContext struct {
|
||||||
|
Config config.Config
|
||||||
|
UserUsecase usecase.UserUsecase
|
||||||
|
AccountUsecase usecase.AccountUsecase
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServiceContext(c config.Config) *ServiceContext {
|
||||||
|
db := mongo.NewClient(c.Mongo.URI)
|
||||||
|
redisClient := redis.NewClient(c.Redis)
|
||||||
|
|
||||||
|
userRepo := repository.NewUserRepository(db)
|
||||||
|
accountRepo := repository.NewAccountRepository(db)
|
||||||
|
|
||||||
|
return &ServiceContext{
|
||||||
|
Config: c,
|
||||||
|
UserUsecase: usecase.NewUserUsecase(userRepo, redisClient),
|
||||||
|
AccountUsecase: usecase.NewAccountUsecase(userRepo, accountRepo, redisClient),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `pkg/repository/` — 基礎設施實作
|
||||||
|
|
||||||
|
```go
|
||||||
|
// pkg/repository/user.go
|
||||||
|
package repository
|
||||||
|
|
||||||
|
type userRepository struct {
|
||||||
|
db *mongo.Database
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserRepository(db *mongo.Database) domain.Repository.UserRepository {
|
||||||
|
return &userRepository{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *userRepository) GetByID(ctx context.Context, id string) (*entity.User, error) {
|
||||||
|
var user entity.User
|
||||||
|
err := r.db.Collection("users").FindOne(ctx, bson.M{"_id": id}).Decode(&user)
|
||||||
|
if err != nil {
|
||||||
|
if err == mongo.ErrNoDocuments {
|
||||||
|
return nil, domain.ErrUserNotFound
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("find user by id: %w", err)
|
||||||
|
}
|
||||||
|
return &user, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
// pkg/repository/user_test.go
|
||||||
|
package repository
|
||||||
|
|
||||||
|
func TestUserRepository_GetByID_Success(t *testing.T) {
|
||||||
|
db := startMongoContainer(t)
|
||||||
|
defer db.Client().Disconnect(context.Background())
|
||||||
|
|
||||||
|
repo := NewUserRepository(db)
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 編碼規範
|
||||||
|
|
||||||
|
### 檔案命名
|
||||||
|
|
||||||
|
```
|
||||||
|
值物件和列舉: pkg/domain/{module}/{name}.go + _test.go
|
||||||
|
Entity: pkg/domain/entity/{name}.go
|
||||||
|
Repository 介面:pkg/domain/repository/{name}.go
|
||||||
|
Usecase 介面: pkg/domain/usecase/{module}.go
|
||||||
|
Usecase 實作: pkg/usecase/{operation}.go + _test.go
|
||||||
|
Usecase 工具: pkg/usecase/{module}_utils.go + _test.go
|
||||||
|
Repository 實作:pkg/repository/{name}.go + _test.go
|
||||||
|
Mock: pkg/mock/repository/{name}.go
|
||||||
|
pkg/mock/usecase/{module}.go
|
||||||
|
Handler 邏輯: internal/logic/{module}/{operation}_logic.go
|
||||||
|
Server: internal/server/{module}/{module}_server.go
|
||||||
|
Service Context:internal/svc/service_context.go
|
||||||
|
Protobuf 定義: generate/protobuf/{module}.proto
|
||||||
|
設定檔: etc/{service}.yaml
|
||||||
|
Dockerfile: build/Dockerfile
|
||||||
|
```
|
||||||
|
|
||||||
|
### 命名規範
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Package: 小寫,語意明確
|
||||||
|
package usecase // 不是 usecases
|
||||||
|
package repository // 不是 repositories
|
||||||
|
package entity // 不是 entities
|
||||||
|
|
||||||
|
// Entity struct: PascalCase,無後綴
|
||||||
|
type User struct { ... } // 不是 UserModel, UserEntity
|
||||||
|
|
||||||
|
// Value Object: 基礎型別別名 + 方法
|
||||||
|
type Status string // 不是 StatusEnum
|
||||||
|
|
||||||
|
// Interface (介面): 放在 pkg/domain/ 下,語意命名
|
||||||
|
type UserRepository interface { ... } // 不是 UserRepo 或 UserRepositoryI
|
||||||
|
|
||||||
|
// Use Case struct: {Module}Usecase
|
||||||
|
type AccountUsecase struct { ... }
|
||||||
|
|
||||||
|
// Use Case 方法: 動詞開頭
|
||||||
|
func (uc *AccountUsecase) CreateUser(ctx context.Context, ...) (*entity.User, error)
|
||||||
|
func (uc *AccountUsecase) GetUser(ctx context.Context, id string) (*entity.User, error)
|
||||||
|
|
||||||
|
// Logic struct: {Operation}Logic
|
||||||
|
type CreateUserLogic struct { ... }
|
||||||
|
|
||||||
|
// Error: Err 前綴
|
||||||
|
var ErrUserNotFound = errors.New("user not found")
|
||||||
|
|
||||||
|
// Constant: PascalCase(exported)或 camelCase(internal)
|
||||||
|
const MaxRetryCount = 3
|
||||||
|
const defaultPageSize = 20
|
||||||
|
```
|
||||||
|
|
||||||
|
### 錯誤處理
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Sentinel errors — pkg/domain/errors.go
|
||||||
|
var (
|
||||||
|
ErrUserNotFound = errors.New("user not found")
|
||||||
|
ErrInvalidInput = errors.New("invalid input")
|
||||||
|
ErrDuplicateEmail = errors.New("email already exists")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Error wrapping — always use %w
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create user: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error checking — always use errors.Is
|
||||||
|
if errors.Is(err, domain.ErrUserNotFound) {
|
||||||
|
// handle not found
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repository errors — pkg/repository/error.go
|
||||||
|
var (
|
||||||
|
ErrMongoConnection = errors.New("mongo connection failed")
|
||||||
|
ErrRedisConnection = errors.New("redis connection failed")
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 介面設計
|
||||||
|
|
||||||
|
```go
|
||||||
|
// 介面定義在 pkg/domain/(消費者端)
|
||||||
|
// 實作定義在 pkg/ 下(提供者端)
|
||||||
|
|
||||||
|
// Accept interfaces, return structs
|
||||||
|
func NewUserUsecase(repo repository.UserRepository, redis *redis.Client) *UserUsecase {
|
||||||
|
return &UserUsecase{repo: repo, redis: redis}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep interfaces small (1-3 methods)
|
||||||
|
type UserRepository interface {
|
||||||
|
GetByID(ctx context.Context, id string) (*entity.User, error)
|
||||||
|
Create(ctx context.Context, user *entity.User) error
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## TDD 規範
|
||||||
|
|
||||||
|
此技能與 `tdd` 技能整合,遵循共同的 TDD 原則。
|
||||||
|
|
||||||
|
### 測試金字塔
|
||||||
|
|
||||||
|
```
|
||||||
|
/\
|
||||||
|
/ \
|
||||||
|
/ E2E \ <- 少數關鍵流程
|
||||||
|
/--------\
|
||||||
|
/Integration\ <- DB + Redis (testcontainers)
|
||||||
|
/--------------\
|
||||||
|
/ Unit Tests \ <- 最多,80%+ 覆蓋
|
||||||
|
/--------------------\
|
||||||
|
```
|
||||||
|
|
||||||
|
### 測試位置
|
||||||
|
|
||||||
|
```
|
||||||
|
測試檔案跟原始碼同目錄:
|
||||||
|
|
||||||
|
pkg/domain/member/status_test.go ← 值物件測試
|
||||||
|
pkg/usecase/create_user_test.go ← Use Case 測試
|
||||||
|
pkg/repository/user_test.go ← Repository 測試
|
||||||
|
pkg/repository/start_mongo_container_test.go ← testcontainers 啟動
|
||||||
|
```
|
||||||
|
|
||||||
|
### 垂直切片 TDD
|
||||||
|
|
||||||
|
每個切片按照以下順序:
|
||||||
|
|
||||||
|
```
|
||||||
|
切片: 使用者註冊
|
||||||
|
1. RED: 寫 TestStatus_IsValid (值物件)
|
||||||
|
2. GREEN: 寫 Status.IsValid()
|
||||||
|
3. RED: 寫 TestAccountUsecase_CreateUser_Success
|
||||||
|
4. GREEN: 寫 domain/entity, domain/usecase介面, pkg/usecase實作, mock
|
||||||
|
5. RED: 寫 TestAccountUsecase_CreateUser_DuplicateEmail
|
||||||
|
6. GREEN: 加入重複檢查
|
||||||
|
7. RED: 寫 TestUserRepository_Create (DB 測試)
|
||||||
|
8. GREEN: 寫 pkg/repository/user.go
|
||||||
|
9. RED: 寫 TestCreateUserLogic (handler 測試)
|
||||||
|
10. GREEN: 寫 internal/logic/account/create_user_logic.go
|
||||||
|
11. REFACTOR: 清理全部
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mock 策略
|
||||||
|
|
||||||
|
```go
|
||||||
|
// 使用 mockery 自動產生 mock
|
||||||
|
// 在接口檔案加上 go:generate 指令
|
||||||
|
//go:generate mockery --name=UserRepository --output=../../mock/repository --outpkg=mock_repository
|
||||||
|
|
||||||
|
// 單元測試使用 mock
|
||||||
|
func TestAccountUsecase_CreateUser_Success(t *testing.T) {
|
||||||
|
mockRepo := new(mock_repository.UserRepository)
|
||||||
|
uc := usecase.NewAccountUsecase(mockRepo, nil, nil)
|
||||||
|
|
||||||
|
mockRepo.On("GetByEmail", mock.Anything, "test@example.com").Return(nil, nil)
|
||||||
|
mockRepo.On("Create", mock.Anything, mock.AnythingOfType("*entity.User")).Return(nil)
|
||||||
|
|
||||||
|
user, err := uc.CreateUser(context.Background(), input)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, user)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### testcontainers 策略
|
||||||
|
|
||||||
|
```go
|
||||||
|
// pkg/repository/start_mongo_container_test.go
|
||||||
|
func startMongoContainer(t *testing.T) *mongo.Database {
|
||||||
|
ctx := context.Background()
|
||||||
|
req := testcontainers.ContainerRequest{
|
||||||
|
Image: "mongo:7",
|
||||||
|
ExposedPorts: []string{"27017/tcp"},
|
||||||
|
WaitingFor: wait.ForListeningPort("27017/tcp"),
|
||||||
|
}
|
||||||
|
|
||||||
|
mongoC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
|
||||||
|
ContainerRequest: req,
|
||||||
|
Started: true,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Cleanup(func() {
|
||||||
|
mongoC.Terminate(ctx)
|
||||||
|
})
|
||||||
|
|
||||||
|
// ... return connected database
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 覆蓋率要求
|
||||||
|
|
||||||
|
- 值物件 (`pkg/domain/member/`): >= 90%
|
||||||
|
- Use Case (`pkg/usecase/`): >= 90%
|
||||||
|
- Repository (`pkg/repository/`): >= 80%
|
||||||
|
- Logic (`internal/logic/`): >= 80%(整合測試為主)
|
||||||
|
- Critical paths: Integration tests required
|
||||||
|
|
||||||
|
## 垂直切片模板
|
||||||
|
|
||||||
|
每個垂直切片的檔案清單:
|
||||||
|
|
||||||
|
```
|
||||||
|
切片: {operation}_{entity}
|
||||||
|
|
||||||
|
新增/修改的檔案:
|
||||||
|
├── pkg/domain/entity/{entity}.go ← Entity 定義
|
||||||
|
├── pkg/domain/member/{value_object}.go ← 值物件(如需)
|
||||||
|
├── pkg/domain/member/{value_object}_test.go ← 值物件測試
|
||||||
|
├── pkg/domain/repository/{entity}.go ← Repository 介面
|
||||||
|
├── pkg/domain/usecase/{module}.go ← Use Case 介面
|
||||||
|
├── pkg/usecase/{operation}.go ← Use Case 實作
|
||||||
|
├── pkg/usecase/{operation}_test.go ← Use Case 測試
|
||||||
|
├── pkg/mock/repository/{entity}.go ← Repository mock
|
||||||
|
├── pkg/repository/{entity}.go ← Repository 實作
|
||||||
|
├── pkg/repository/{entity}_test.go ← Repository 測試
|
||||||
|
├── internal/logic/{module}/{operation}_logic.go ← Handler 邏輯
|
||||||
|
└── internal/svc/service_context.go ← 更新 DI
|
||||||
|
```
|
||||||
|
|
||||||
|
## 完成檢查清單
|
||||||
|
|
||||||
|
### 每個切片完成後
|
||||||
|
- [ ] 值物件測試通過
|
||||||
|
- [ ] Use Case 測試通過
|
||||||
|
- [ ] Repository 測試通過(含 DB)
|
||||||
|
- [ ] 錯誤處理完整
|
||||||
|
- [ ] 依賴方向正確(domain 無外部依賴)
|
||||||
|
|
||||||
|
### 全部完成後
|
||||||
|
- [ ] 專案結構符合 Domain-Driven + go-zero 風格
|
||||||
|
- [ ] `pkg/domain/` 包含所有 Entity、Value Object、介面定義
|
||||||
|
- [ ] `pkg/usecase/` 包含所有業務邏輯實作
|
||||||
|
- [ ] `pkg/repository/` 包含所有基礎設施實作
|
||||||
|
- [ ] `internal/logic/` 包含所有 Handler 邏輯
|
||||||
|
- [ ] `internal/svc/` 包含完整的依賴注入設定
|
||||||
|
- [ ] 單元測試 >= 80% 覆蓋率
|
||||||
|
- [ ] 業務邏輯 >= 90% 覆蓋率
|
||||||
|
- [ ] 整合測試通過(關鍵路徑)
|
||||||
|
- [ ] 錯誤處理一致且使用 `%w` wrapping
|
||||||
|
|
||||||
|
## 相依技能
|
||||||
|
|
||||||
|
- **前置**: `prd-to-plan` (實作計畫), `be-api-design` (API 規格), `dba-schema` (DB Schema)
|
||||||
|
- **輔助**: `tdd` (TDD Red-Green-Refactor 流程), `design-an-interface` (介面設計)
|
||||||
|
- **後續**: `qa` (QA 測試)
|
||||||
|
|
||||||
|
## 退回機制
|
||||||
|
|
||||||
|
```
|
||||||
|
QA 失敗 (Stage 10)
|
||||||
|
↓
|
||||||
|
Orchestrator 重新分配修復任務
|
||||||
|
↓
|
||||||
|
Backend Agent 修復 Bug + 新增回歸測試
|
||||||
|
↓
|
||||||
|
重新進入 QA (Stage 10)
|
||||||
|
|
||||||
|
Code Review 退回 (Stage 11)
|
||||||
|
↓
|
||||||
|
處理 PR 回饋
|
||||||
|
↓
|
||||||
|
重新進入 QA (Stage 10) 驗證
|
||||||
|
|
||||||
|
實作計畫不可行
|
||||||
|
↓
|
||||||
|
退回 Task Breakdown (Stage 8) 重新分解
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,296 @@
|
||||||
|
---
|
||||||
|
name: tdd
|
||||||
|
description: "Backend Agent 使用此技能進行測試驅動開發。遵循 Red-Green-Refactor 循環和垂直切片原則,確保測試覆蓋行為而非實作細節。觸發時機:實作階段(Stage 9),由 go-backend-dev 技能整合使用。"
|
||||||
|
---
|
||||||
|
|
||||||
|
# /tdd — 測試驅動開發
|
||||||
|
|
||||||
|
Backend Agent 使用此技能進行測試驅動開發。
|
||||||
|
|
||||||
|
## 核心理念
|
||||||
|
|
||||||
|
**測試行為,而非實作細節。**
|
||||||
|
|
||||||
|
好的測試透過公開介面驗證行為,描述系統「做什麼」而非「怎麼做」。重構後測試仍然通過。
|
||||||
|
|
||||||
|
壞的測試與實作耦合:mock 內部協作者、測試私有方法。重構後測試失敗,但行為沒有改變。
|
||||||
|
|
||||||
|
## 反模式:水平切片
|
||||||
|
|
||||||
|
**不要先寫完所有測試再寫所有實作。** 這是「水平切片」:
|
||||||
|
|
||||||
|
```
|
||||||
|
❌ 錯誤方式:
|
||||||
|
RED: test1, test2, test3, test4, test5
|
||||||
|
GREEN: impl1, impl2, impl3, impl4, impl5
|
||||||
|
|
||||||
|
✅ 正確方式(垂直切片):
|
||||||
|
RED→GREEN: test1 → impl1
|
||||||
|
RED→GREEN: test2 → impl2
|
||||||
|
RED→GREEN: test3 → impl3
|
||||||
|
```
|
||||||
|
|
||||||
|
水平切片會產生劣質測試:
|
||||||
|
- 大量寫的測試驗證「想像的」行為,而非「實際的」行為
|
||||||
|
- 測試會變成驗證資料結構和函式簽名,而非使用者可觀察的行為
|
||||||
|
- 測試對真正的變更不敏感 — 行為壞了還是通過,行為沒變但重構後卻失敗
|
||||||
|
|
||||||
|
## 流程
|
||||||
|
|
||||||
|
```
|
||||||
|
確認介面變更與測試範圍
|
||||||
|
↓
|
||||||
|
寫第一個測試(tracer bullet)
|
||||||
|
↓
|
||||||
|
RED: 測試失敗
|
||||||
|
↓
|
||||||
|
GREEN: 寫最少程式碼讓測試通過
|
||||||
|
↓
|
||||||
|
寫下一個測試
|
||||||
|
↓
|
||||||
|
RED → GREEN 循環
|
||||||
|
↓
|
||||||
|
所有行為測試完成
|
||||||
|
↓
|
||||||
|
REFACTOR: 重構
|
||||||
|
↓
|
||||||
|
確認所有測試仍然通過
|
||||||
|
```
|
||||||
|
|
||||||
|
### 步驟說明
|
||||||
|
|
||||||
|
**1. 規劃**
|
||||||
|
|
||||||
|
在寫任何程式碼之前:
|
||||||
|
- [ ] 與使用者確認需要哪些介面變更
|
||||||
|
- [ ] 確認哪些行為需要測試(排序優先順序)
|
||||||
|
- [ ] 識別深模組的機會(小介面,深實作)
|
||||||
|
- [ ] 為可測試性設計介面
|
||||||
|
- [ ] 列出要測試的行為(不是實作步驟)
|
||||||
|
- [ ] 取得使用者對測試計畫的認可
|
||||||
|
|
||||||
|
提問:「公開介面應該長什麼樣子?哪些行為最重要需要測試?」
|
||||||
|
|
||||||
|
**你不可能測試所有東西。** 與使用者確認哪些行為最重要,將測試精力集中在關鍵路徑和複雜邏輯,而不是每個可能的邊緣案例。
|
||||||
|
|
||||||
|
**2. Tracer Bullet**
|
||||||
|
|
||||||
|
寫一個測試,確認系統的一件事:
|
||||||
|
|
||||||
|
```
|
||||||
|
RED: 寫第一個行為的測試 → 測試失敗
|
||||||
|
GREEN: 寫最少的程式碼讓測試通過 → 測試通過
|
||||||
|
```
|
||||||
|
|
||||||
|
這是你的 tracer bullet — 證明端到端路徑可行。
|
||||||
|
|
||||||
|
**3. 遞增循環**
|
||||||
|
|
||||||
|
對每個剩餘行為:
|
||||||
|
|
||||||
|
```
|
||||||
|
RED: 寫下一個測試 → 失敗
|
||||||
|
GREEN: 最少程式碼讓測試通過 → 通過
|
||||||
|
```
|
||||||
|
|
||||||
|
規則:
|
||||||
|
- 一次一個測試
|
||||||
|
- 只寫足夠讓當前測試通過的程式碼
|
||||||
|
- 不要預測未來的測試
|
||||||
|
- 測試聚焦在可觀察的行為
|
||||||
|
|
||||||
|
**4. 重構**
|
||||||
|
|
||||||
|
所有測試通過後,尋找重構候選:
|
||||||
|
- [ ] 提取重複邏輯
|
||||||
|
- [ ] 加深模組(將複雜度移到簡單介面後方)
|
||||||
|
- [ ] 自然地應用 SOLID 原則
|
||||||
|
- [ ] 思考新程式碼揭示了什麼既有程式碼的問題
|
||||||
|
- [ ] 每個重構步驟後都跑測試
|
||||||
|
|
||||||
|
**絕對不要在 RED 狀態下重構。先回到 GREEN。**
|
||||||
|
|
||||||
|
## 好的測試 vs 壞的測試
|
||||||
|
|
||||||
|
### 好的測試
|
||||||
|
|
||||||
|
**整合風格**:透過真實介面測試,不是 mock 內部零件。
|
||||||
|
|
||||||
|
```go
|
||||||
|
// GOOD: 測試可觀察的行為
|
||||||
|
func TestUserUsecase_CreateUser_Success(t *testing.T) {
|
||||||
|
mockRepo := new(mock.UserRepository)
|
||||||
|
uc := NewUserUsecase(mockRepo, logger)
|
||||||
|
|
||||||
|
mockRepo.On("GetByEmail", mock.Anything, "test@example.com").Return(nil, nil)
|
||||||
|
mockRepo.On("Create", mock.Anything, mock.AnythingOfType("*domain.User")).Return(nil)
|
||||||
|
|
||||||
|
user, err := uc.CreateUser(context.Background(), input)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, user)
|
||||||
|
assert.Equal(t, "test@example.com", user.Email)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
特徵:
|
||||||
|
- 測試使用者/呼叫者關心的行為
|
||||||
|
- 只使用公開 API
|
||||||
|
- 重構內部實作後測試仍然通過
|
||||||
|
- 描述「做什麼」而非「怎麼做」
|
||||||
|
- 每個測試一個邏輯斷言
|
||||||
|
|
||||||
|
### 壊的測試
|
||||||
|
|
||||||
|
**實作細節測試**:與內部結構耦合。
|
||||||
|
|
||||||
|
```go
|
||||||
|
// BAD: 測試實作細節
|
||||||
|
func TestUserUsecase_CreateUser_CallsRepoCreate(t *testing.T) {
|
||||||
|
mockRepo := new(mock.UserRepository)
|
||||||
|
uc := NewUserUsecase(mockRepo, logger)
|
||||||
|
|
||||||
|
uc.CreateUser(context.Background(), input)
|
||||||
|
|
||||||
|
// 這測試的是「怎麼做」而非「做什麼」
|
||||||
|
mockRepo.AssertCalled(t, "Create", mock.Anything, mock.Anything)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
紅旗:
|
||||||
|
- Mock 內部協作者只是為了驗證被呼叫
|
||||||
|
- 測試私有方法
|
||||||
|
- 斷言呼叫次數或順序
|
||||||
|
- 重構後測試失敗但行為沒變
|
||||||
|
- 測試名稱描述「怎麼做」而非「做什麼」
|
||||||
|
|
||||||
|
```go
|
||||||
|
// BAD: 繞過介面驗證
|
||||||
|
func TestCreateUser_SavesToDatabase(t *testing.T) {
|
||||||
|
CreateUser(ctx, input)
|
||||||
|
row := db.QueryRow("SELECT * FROM users WHERE name = $1", "Alice")
|
||||||
|
// 直接查資料庫驗證,繞過公開介面
|
||||||
|
}
|
||||||
|
|
||||||
|
// GOOD: 透過介面驗證
|
||||||
|
func TestCreateUser_MakesUserRetrievable(t *testing.T) {
|
||||||
|
user, _ := CreateUser(ctx, input)
|
||||||
|
retrieved, _ := GetUser(ctx, user.ID)
|
||||||
|
assert.Equal(t, "Alice", retrieved.Name)
|
||||||
|
// 透過公開介面驗證行為
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Golang 測試規範
|
||||||
|
|
||||||
|
### 測試命名
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Test{Unit}_{Scenario}
|
||||||
|
func TestUserUsecase_CreateUser_Success(t *testing.T) {}
|
||||||
|
func TestUserUsecase_CreateUser_InvalidEmail(t *testing.T) {}
|
||||||
|
func TestUserUsecase_CreateUser_Duplicate(t *testing.T) {}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 測試金字塔
|
||||||
|
|
||||||
|
```
|
||||||
|
/\
|
||||||
|
/ \
|
||||||
|
/ E2E \ <- 少數關鍵流程
|
||||||
|
/--------\
|
||||||
|
/Integration\ <- API + DB
|
||||||
|
/--------------\
|
||||||
|
/ Unit Tests \ <- 最多,80%+ 覆蓋
|
||||||
|
/--------------------\
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mock 策略
|
||||||
|
|
||||||
|
只在**系統邊界** mock:
|
||||||
|
- 外部 API(支付、郵件等)
|
||||||
|
- 資料庫(有時 — 優先使用測試 DB)
|
||||||
|
- 時間/隨機性
|
||||||
|
- 檔案系統(有時)
|
||||||
|
|
||||||
|
不要 mock:
|
||||||
|
- 你自己的類別/模組
|
||||||
|
- 內部協作者
|
||||||
|
- 你可以控制的東西
|
||||||
|
|
||||||
|
```go
|
||||||
|
// 使用 mockery 自動產生 mock
|
||||||
|
//go:generate mockery --name=UserRepository
|
||||||
|
|
||||||
|
// 單元測試使用 mock repo
|
||||||
|
func TestUserUsecase_CreateUser_Success(t *testing.T) {
|
||||||
|
mockRepo := new(mock.UserRepository)
|
||||||
|
uc := NewUserUsecase(mockRepo, logger)
|
||||||
|
|
||||||
|
mockRepo.On("GetByEmail", mock.Anything, "test@example.com").Return(nil, nil)
|
||||||
|
mockRepo.On("Create", mock.Anything, mock.AnythingOfType("*domain.User")).Return(nil)
|
||||||
|
|
||||||
|
user, err := uc.CreateUser(context.Background(), input)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, user)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 介面設計的可測試性
|
||||||
|
|
||||||
|
好的介面讓測試自然:
|
||||||
|
|
||||||
|
**1. 接受依賴,不要建立依賴**
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Testable
|
||||||
|
func (s *UserService) CreateUser(ctx context.Context, input CreateUserInput, repo UserRepository) (*User, error) {}
|
||||||
|
|
||||||
|
// Hard to test
|
||||||
|
func (s *UserService) CreateUser(ctx context.Context, input CreateUserInput) (*User, error) {
|
||||||
|
repo := postgres.NewUserRepository(db) // 建立依賴
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. 回傳結果,不要產生副作用**
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Testable
|
||||||
|
func CalculateDiscount(cart *Cart) Discount {}
|
||||||
|
|
||||||
|
// Hard to test
|
||||||
|
func ApplyDiscount(cart *Cart) {
|
||||||
|
cart.Total -= discount // 修改輸入
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. 小介面面積**
|
||||||
|
- 方法少 = 測試少
|
||||||
|
- 參數少 = 測試設定簡單
|
||||||
|
|
||||||
|
## 每個循環的檢查清單
|
||||||
|
|
||||||
|
```
|
||||||
|
[ ] 測試描述行為,而非實作
|
||||||
|
[ ] 測試只使用公開介面
|
||||||
|
[ ] 測試在內部重構後仍然通過
|
||||||
|
[ ] 程式碼是讓當前測試通過的最少實作
|
||||||
|
[ ] 沒有投機性的功能
|
||||||
|
```
|
||||||
|
|
||||||
|
## 重構候選
|
||||||
|
|
||||||
|
TDD 循環完成後,尋找:
|
||||||
|
- **重複邏輯** → 提取 function / class
|
||||||
|
- **過長方法** → 拆成私有 helper(保持測試在公開介面)
|
||||||
|
- **淺模組** → 合併或加深
|
||||||
|
- **Feature envy** → 把邏輯移到資料所在的地方
|
||||||
|
- **原始型別偏執** → 引入 value object
|
||||||
|
- **新程式碼揭示的既有程式碼問題**
|
||||||
|
|
||||||
|
## 相依技能
|
||||||
|
|
||||||
|
- **前置**: `go-backend-dev` (在實作中整合使用)
|
||||||
|
- **輔助**: `design-an-interface` (為可測試性設計介面)
|
||||||
|
- **後續**: `qa` (QA 測試)
|
||||||
|
|
@ -0,0 +1,532 @@
|
||||||
|
---
|
||||||
|
name: be-api-design
|
||||||
|
description: "Backend Agent uses this skill to design API specs. Based on PRD, produce OpenAPI 3.0 spec including endpoints, request/response structures, error handling. Trigger: After PRD approved (Stage 4)."
|
||||||
|
---
|
||||||
|
|
||||||
|
# /be-api-design — API Design
|
||||||
|
|
||||||
|
Backend Agent uses this skill to design API specs.
|
||||||
|
|
||||||
|
## Responsibilities
|
||||||
|
|
||||||
|
1. Analyze functional requirements from PRD
|
||||||
|
2. Use `design-an-interface` to explore multiple API design options
|
||||||
|
3. Design RESTful API endpoints
|
||||||
|
4. Define request/response schemas
|
||||||
|
5. Design error handling mechanism
|
||||||
|
6. Produce OpenAPI 3.0 spec
|
||||||
|
|
||||||
|
## Input
|
||||||
|
|
||||||
|
- PRD document (`docs/prd/{date}-{feature}.md`)
|
||||||
|
- Existing API style (existing API specs in project)
|
||||||
|
- Data model context
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
- API spec document: `docs/api/{date}-{feature}.yaml` (OpenAPI 3.0)
|
||||||
|
|
||||||
|
## Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
Read PRD
|
||||||
|
↓
|
||||||
|
Identify resources and operations
|
||||||
|
↓
|
||||||
|
Call design-an-interface to explore 2-3 design options
|
||||||
|
↓
|
||||||
|
Select best option
|
||||||
|
↓
|
||||||
|
Define OpenAPI Schema
|
||||||
|
↓
|
||||||
|
Design error handling
|
||||||
|
↓
|
||||||
|
Security review
|
||||||
|
↓
|
||||||
|
Produce OpenAPI document
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step Details
|
||||||
|
|
||||||
|
**1. Read PRD**
|
||||||
|
|
||||||
|
Extract all functional requirements from PRD, identify:
|
||||||
|
- Resources (nouns): users, orders, products, etc.
|
||||||
|
- Operations (verbs): create, read, update, delete, etc.
|
||||||
|
- Relationships: relationships between resources (one-to-many, many-to-many)
|
||||||
|
- Non-functional requirements: pagination, rate limiting, authentication, etc.
|
||||||
|
|
||||||
|
**2. Identify Resources and Operations**
|
||||||
|
|
||||||
|
Map functional requirements to RESTful resources:
|
||||||
|
- Each noun → potential resource
|
||||||
|
- Each verb → HTTP method
|
||||||
|
- Each relationship → nested resource or standalone endpoint
|
||||||
|
|
||||||
|
**3. Call design-an-interface**
|
||||||
|
|
||||||
|
Use `design-an-interface` skill to generate 2-3 distinct API design options:
|
||||||
|
- Option A: Minimize method count (1-3 endpoints per resource)
|
||||||
|
- Option B: Maximize flexibility (support multiple use cases)
|
||||||
|
- Option C: Optimize for most common operations
|
||||||
|
|
||||||
|
Compare pros and cons, select best design.
|
||||||
|
|
||||||
|
**4. Define OpenAPI Schema**
|
||||||
|
|
||||||
|
Use template below to produce complete OpenAPI 3.0 spec.
|
||||||
|
|
||||||
|
**5. Security Review**
|
||||||
|
|
||||||
|
Ensure all endpoints have appropriate authentication and permission controls.
|
||||||
|
|
||||||
|
**6. Produce Document**
|
||||||
|
|
||||||
|
Save to `docs/api/{date}-{feature}.yaml`.
|
||||||
|
|
||||||
|
## Design Principles
|
||||||
|
|
||||||
|
### RESTful Design
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Resource-oriented:
|
||||||
|
- URL represents resource, not action
|
||||||
|
- HTTP methods represent actions
|
||||||
|
|
||||||
|
Example:
|
||||||
|
GET /api/v1/users # List users
|
||||||
|
GET /api/v1/users/{id} # Get specific user
|
||||||
|
POST /api/v1/users # Create user
|
||||||
|
PUT /api/v1/users/{id} # Full update
|
||||||
|
PATCH /api/v1/users/{id} # Partial update
|
||||||
|
DELETE /api/v1/users/{id} # Delete user
|
||||||
|
|
||||||
|
Nested resources:
|
||||||
|
GET /api/v1/users/{id}/orders # Get user's orders
|
||||||
|
POST /api/v1/users/{id}/orders # Create order for user
|
||||||
|
```
|
||||||
|
|
||||||
|
### HTTP Status Codes
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Success:
|
||||||
|
200: OK # GET, PUT, PATCH, DELETE success
|
||||||
|
201: Created # POST successfully created resource
|
||||||
|
204: No Content # DELETE success, no response body
|
||||||
|
|
||||||
|
Client Error:
|
||||||
|
400: Bad Request # Request format error
|
||||||
|
401: Unauthorized # Not authenticated
|
||||||
|
403: Forbidden # No permission
|
||||||
|
404: Not Found # Resource not found
|
||||||
|
409: Conflict # Resource conflict (e.g., duplicate)
|
||||||
|
422: Unprocessable Entity # Validation error
|
||||||
|
429: Too Many Requests # Rate limit
|
||||||
|
|
||||||
|
Server Error:
|
||||||
|
500: Internal Server Error # Server internal error
|
||||||
|
502: Bad Gateway # Upstream service error
|
||||||
|
503: Service Unavailable # Service temporarily unavailable
|
||||||
|
```
|
||||||
|
|
||||||
|
### Response Format
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Success response:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
type: object
|
||||||
|
meta:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
page:
|
||||||
|
type: integer
|
||||||
|
limit:
|
||||||
|
type: integer
|
||||||
|
total:
|
||||||
|
type: integer
|
||||||
|
total_pages:
|
||||||
|
type: integer
|
||||||
|
|
||||||
|
Error response:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
error:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
code:
|
||||||
|
type: string
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
details:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
field:
|
||||||
|
type: string
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
```
|
||||||
|
|
||||||
|
## OpenAPI 3.0 Template
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
openapi: 3.0.3
|
||||||
|
info:
|
||||||
|
title: {API Name}
|
||||||
|
version: 1.0.0
|
||||||
|
description: |
|
||||||
|
{Description}
|
||||||
|
|
||||||
|
Related PRD: {PRD Link}
|
||||||
|
|
||||||
|
servers:
|
||||||
|
- url: https://api.example.com/v1
|
||||||
|
description: Production
|
||||||
|
- url: https://staging-api.example.com/v1
|
||||||
|
description: Staging
|
||||||
|
|
||||||
|
paths:
|
||||||
|
/users:
|
||||||
|
get:
|
||||||
|
summary: List users
|
||||||
|
tags:
|
||||||
|
- Users
|
||||||
|
parameters:
|
||||||
|
- name: page
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
default: 1
|
||||||
|
- name: limit
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
default: 20
|
||||||
|
maximum: 100
|
||||||
|
- name: search
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Success
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/UserListResponse'
|
||||||
|
'401':
|
||||||
|
$ref: '#/components/responses/Unauthorized'
|
||||||
|
'500':
|
||||||
|
$ref: '#/components/responses/InternalError'
|
||||||
|
|
||||||
|
post:
|
||||||
|
summary: Create user
|
||||||
|
tags:
|
||||||
|
- Users
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/CreateUserRequest'
|
||||||
|
responses:
|
||||||
|
'201':
|
||||||
|
description: Created successfully
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/UserResponse'
|
||||||
|
'400':
|
||||||
|
$ref: '#/components/responses/BadRequest'
|
||||||
|
'422':
|
||||||
|
$ref: '#/components/responses/ValidationError'
|
||||||
|
|
||||||
|
/users/{id}:
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
|
||||||
|
get:
|
||||||
|
summary: Get user
|
||||||
|
tags:
|
||||||
|
- Users
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Success
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/UserResponse'
|
||||||
|
'404':
|
||||||
|
$ref: '#/components/responses/NotFound'
|
||||||
|
|
||||||
|
put:
|
||||||
|
summary: Full update user
|
||||||
|
tags:
|
||||||
|
- Users
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/UpdateUserRequest'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Updated successfully
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/UserResponse'
|
||||||
|
'404':
|
||||||
|
$ref: '#/components/responses/NotFound'
|
||||||
|
'422':
|
||||||
|
$ref: '#/components/responses/ValidationError'
|
||||||
|
|
||||||
|
patch:
|
||||||
|
summary: Partial update user
|
||||||
|
tags:
|
||||||
|
- Users
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/PatchUserRequest'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Updated successfully
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/UserResponse'
|
||||||
|
'404':
|
||||||
|
$ref: '#/components/responses/NotFound'
|
||||||
|
|
||||||
|
delete:
|
||||||
|
summary: Delete user
|
||||||
|
tags:
|
||||||
|
- Users
|
||||||
|
responses:
|
||||||
|
'204':
|
||||||
|
description: Deleted successfully
|
||||||
|
'404':
|
||||||
|
$ref: '#/components/responses/NotFound'
|
||||||
|
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
User:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
example: "usr_123456"
|
||||||
|
email:
|
||||||
|
type: string
|
||||||
|
format: email
|
||||||
|
example: "user@example.com"
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
example: "John Doe"
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
enum: [active, inactive, suspended]
|
||||||
|
example: "active"
|
||||||
|
created_at:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
updated_at:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- email
|
||||||
|
- name
|
||||||
|
- status
|
||||||
|
- created_at
|
||||||
|
- updated_at
|
||||||
|
|
||||||
|
CreateUserRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
email:
|
||||||
|
type: string
|
||||||
|
format: email
|
||||||
|
password:
|
||||||
|
type: string
|
||||||
|
minLength: 8
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
minLength: 1
|
||||||
|
maxLength: 100
|
||||||
|
required:
|
||||||
|
- email
|
||||||
|
- password
|
||||||
|
- name
|
||||||
|
|
||||||
|
UpdateUserRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
email:
|
||||||
|
type: string
|
||||||
|
format: email
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
minLength: 1
|
||||||
|
maxLength: 100
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
enum: [active, inactive]
|
||||||
|
|
||||||
|
PatchUserRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
minLength: 1
|
||||||
|
maxLength: 100
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
enum: [active, inactive]
|
||||||
|
|
||||||
|
UserResponse:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/components/schemas/User'
|
||||||
|
|
||||||
|
UserListResponse:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/User'
|
||||||
|
meta:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
page:
|
||||||
|
type: integer
|
||||||
|
limit:
|
||||||
|
type: integer
|
||||||
|
total:
|
||||||
|
type: integer
|
||||||
|
total_pages:
|
||||||
|
type: integer
|
||||||
|
|
||||||
|
Error:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
error:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
code:
|
||||||
|
type: string
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
details:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
field:
|
||||||
|
type: string
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
|
||||||
|
responses:
|
||||||
|
BadRequest:
|
||||||
|
description: Request format error
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Error'
|
||||||
|
|
||||||
|
Unauthorized:
|
||||||
|
description: Not authenticated
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Error'
|
||||||
|
|
||||||
|
NotFound:
|
||||||
|
description: Resource not found
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Error'
|
||||||
|
|
||||||
|
ValidationError:
|
||||||
|
description: Validation error
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Error'
|
||||||
|
|
||||||
|
InternalError:
|
||||||
|
description: Server internal error
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Error'
|
||||||
|
|
||||||
|
securitySchemes:
|
||||||
|
bearerAuth:
|
||||||
|
type: http
|
||||||
|
scheme: bearer
|
||||||
|
bearerFormat: JWT
|
||||||
|
|
||||||
|
security:
|
||||||
|
- bearerAuth: []
|
||||||
|
```
|
||||||
|
|
||||||
|
## Design Checklist
|
||||||
|
|
||||||
|
### Security
|
||||||
|
- [ ] All endpoints have appropriate authentication
|
||||||
|
- [ ] Input validation complete
|
||||||
|
- [ ] Rate limiting designed
|
||||||
|
- [ ] CORS configured
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
- [ ] Pagination support
|
||||||
|
- [ ] Caching strategy
|
||||||
|
- [ ] Batch operation support
|
||||||
|
|
||||||
|
### Reliability
|
||||||
|
- [ ] Error response format unified
|
||||||
|
- [ ] Retry mechanism recommendations
|
||||||
|
- [ ] Idempotency design
|
||||||
|
|
||||||
|
### Consistency
|
||||||
|
- [ ] Naming convention consistent (resources use plural nouns)
|
||||||
|
- [ ] Response format consistent (data/meta/error structure)
|
||||||
|
- [ ] Filter/sort/pagination parameters consistent
|
||||||
|
|
||||||
|
## Related Skills
|
||||||
|
|
||||||
|
- **Prerequisite**: `write-a-prd` (after PRD approved)
|
||||||
|
- **辅助**: `design-an-interface` (explore multiple design options)
|
||||||
|
- **Follow-up**: `dba-schema` (DB Schema design)
|
||||||
|
- **Rollback**: Can rollback to `write-a-prd` to modify requirements
|
||||||
|
|
||||||
|
## Rollback Mechanism
|
||||||
|
|
||||||
|
```
|
||||||
|
Design Review rejects API design
|
||||||
|
↓
|
||||||
|
Modify OpenAPI spec
|
||||||
|
↓
|
||||||
|
Re-submit (or re-call design-an-interface)
|
||||||
|
|
||||||
|
DBA Agent finds Schema conflict
|
||||||
|
↓
|
||||||
|
Negotiate domain model or API response format adjustments
|
||||||
|
↓
|
||||||
|
Update OpenAPI spec
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,188 @@
|
||||||
|
---
|
||||||
|
name: design-an-interface
|
||||||
|
description: "Backend Agent uses this skill to explore multiple API interface design options. Based on 'Design It Twice' principle, generate multiple distinct designs, compare, and select the best option. Trigger: API design phase (Stage 4), called by be-api-design skill."
|
||||||
|
---
|
||||||
|
|
||||||
|
# /design-an-interface — Interface Design Exploration
|
||||||
|
|
||||||
|
Backend Agent uses this skill to explore API interface design options.
|
||||||
|
|
||||||
|
Based on "Design It Twice" principle from "A Philosophy of Software Design": The first idea is usually not the best. Generate multiple distinct designs, then compare and choose.
|
||||||
|
|
||||||
|
## Responsibilities
|
||||||
|
|
||||||
|
1. Generate 2-3 distinct interface design options for module requirements
|
||||||
|
2. Each option uses different design constraints
|
||||||
|
3. Compare pros and cons of options
|
||||||
|
4. Assist in selecting or synthesizing the best option
|
||||||
|
|
||||||
|
## Input
|
||||||
|
|
||||||
|
- Module description (functional requirements from PRD)
|
||||||
|
- User stories and usage scenarios
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
- Multiple interface design options (including interface signatures, usage examples, pros/cons analysis)
|
||||||
|
- Option comparison and recommendations
|
||||||
|
|
||||||
|
## Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
Collect requirements
|
||||||
|
↓
|
||||||
|
Generate 2-3 design options (parallel sub-agents)
|
||||||
|
↓
|
||||||
|
Present each option
|
||||||
|
↓
|
||||||
|
Compare options (interface simplicity, generality, implementation efficiency, depth)
|
||||||
|
↓
|
||||||
|
Synthesize best option or select most suitable
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step Details
|
||||||
|
|
||||||
|
**1. Collect Requirements**
|
||||||
|
|
||||||
|
Before designing, understand:
|
||||||
|
- [ ] What problem does this module solve?
|
||||||
|
- [ ] Who are the callers? (other modules, external users, tests)
|
||||||
|
- [ ] What are the key operations?
|
||||||
|
- [ ] What are the constraints? (performance, compatibility, existing patterns)
|
||||||
|
- [ ] What should be hidden internally? What should be exposed externally?
|
||||||
|
|
||||||
|
Ask: "What does this module need to do? Who will use it?"
|
||||||
|
|
||||||
|
**2. Generate Design Options (Parallel Sub-Agents)**
|
||||||
|
|
||||||
|
Generate 3+ distinct options simultaneously. Each option must follow different constraints:
|
||||||
|
|
||||||
|
```
|
||||||
|
Option A: Minimize method count — target 1-3 methods
|
||||||
|
Option B: Maximize flexibility — support multiple use cases
|
||||||
|
Option C: Optimize for most common operations
|
||||||
|
Option D (optional): Reference specific paradigm or framework design
|
||||||
|
```
|
||||||
|
|
||||||
|
Each option should include:
|
||||||
|
1. Interface signature (types/methods)
|
||||||
|
2. Usage examples (how callers actually use it)
|
||||||
|
3. What complexity this design hides
|
||||||
|
4. Trade-offs of this option
|
||||||
|
|
||||||
|
**3. Present Options**
|
||||||
|
|
||||||
|
Present each option one by one, including:
|
||||||
|
- Interface signature: types, methods, params
|
||||||
|
- Usage examples: how callers actually use it
|
||||||
|
- Hidden complexity: small interface hiding large implementation (good) vs large interface with thin implementation (bad)
|
||||||
|
|
||||||
|
Allow user to fully absorb each option before comparison.
|
||||||
|
|
||||||
|
**4. Compare Options**
|
||||||
|
|
||||||
|
Compare across dimensions:
|
||||||
|
|
||||||
|
- **Interface simplicity**: Few methods, simple parameters → Easier to learn and use correctly
|
||||||
|
- **Generality vs Specificity**: Flexibility vs focus, where are the trade-offs
|
||||||
|
- **Implementation efficiency**: Does interface shape allow efficient internal implementation?
|
||||||
|
- **Depth**: Small interface hiding large complexity (deep module, good) vs large interface with thin implementation (shallow module, bad)
|
||||||
|
- **Ease of correct usage vs ease of misuse**
|
||||||
|
|
||||||
|
Discuss trade-offs in text, not just tables. Emphasize where options diverge most.
|
||||||
|
|
||||||
|
**5. Synthesize Best Option**
|
||||||
|
|
||||||
|
The best design often combines insights from multiple options. Ask:
|
||||||
|
- "Which option fits your primary use case best?"
|
||||||
|
- "Are there elements from other options worth incorporating?"
|
||||||
|
|
||||||
|
## Evaluation Criteria
|
||||||
|
|
||||||
|
From "A Philosophy of Software Design":
|
||||||
|
|
||||||
|
**Interface simplicity**: Few methods, simple parameters = Easier to learn and use correctly.
|
||||||
|
|
||||||
|
**Generality**: Can handle future use cases without modification. But avoid over-generalization.
|
||||||
|
|
||||||
|
**Implementation efficiency**: Does interface shape allow efficient implementation? Or does it force awkward internal implementation?
|
||||||
|
|
||||||
|
**Depth**: Small interface hiding large complexity = deep module (good). Large interface with thin implementation = shallow module (avoid).
|
||||||
|
|
||||||
|
```
|
||||||
|
Deep module (good):
|
||||||
|
┌─────────────────────┐
|
||||||
|
│ Small Interface │ ← Few methods, simple parameters
|
||||||
|
├─────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ Deep Implementation│ ← Complex logic hidden inside
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
└─────────────────────┘
|
||||||
|
|
||||||
|
Shallow module (avoid):
|
||||||
|
┌─────────────────────────────────┐
|
||||||
|
│ Large Interface │ ← Many methods, complex parameters
|
||||||
|
├─────────────────────────────────┤
|
||||||
|
│ Thin Implementation │ ← Just a pass-through
|
||||||
|
└─────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Anti-Patterns
|
||||||
|
|
||||||
|
- Don't let sub-agents generate similar designs — force distinct ones
|
||||||
|
- Don't skip comparison — value comes from contrast
|
||||||
|
- Don't implement at this stage — this is pure interface design
|
||||||
|
- Don't evaluate options based on implementation effort — only look at interface quality
|
||||||
|
|
||||||
|
## Golang Interface Design Examples
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Option A: Minimize method count
|
||||||
|
type UserRepository interface {
|
||||||
|
GetByID(ctx context.Context, id string) (*domain.User, error)
|
||||||
|
Save(ctx context.Context, user *domain.User) error
|
||||||
|
}
|
||||||
|
// Pros: Simple, easy to mock
|
||||||
|
// Cons: Save handles both Create and Update, depends on whether it exists
|
||||||
|
|
||||||
|
// Option B: Separate read/write
|
||||||
|
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
|
||||||
|
}
|
||||||
|
// Pros: CQRS friendly, separated concerns
|
||||||
|
// Cons: More methods, but each method has clearer semantics
|
||||||
|
|
||||||
|
// Option C: Optimize for common operations
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
// Pros: Directly maps to business operations, most intuitive
|
||||||
|
// Cons: Need to add method for each new operation, less flexible
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integration with be-api-design
|
||||||
|
|
||||||
|
This skill is automatically called by `be-api-design` at step 3. API design flow:
|
||||||
|
|
||||||
|
```
|
||||||
|
be-api-design step 1: Read PRD
|
||||||
|
be-api-design step 2: Identify resources and operations
|
||||||
|
→ design-an-interface: Explore 2-3 API design options
|
||||||
|
be-api-design step 4: Select option, define OpenAPI spec
|
||||||
|
```
|
||||||
|
|
||||||
|
## Related Skills
|
||||||
|
|
||||||
|
- **Prerequisite**: `write-a-prd` (PRD complete)
|
||||||
|
- **Follow-up**: `be-api-design` (API spec definition)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Abstract Factory Pattern
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
Load this skill when you need to solve design problems described by this pattern in your code.
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
1. Analyze whether current code structure matches the pattern's intent.
|
||||||
|
2. Refactor or implement following the pattern's typical structure.
|
||||||
|
3. Ensure the pattern introduction reduces coupling or improves flexibility.
|
||||||
|
|
||||||
|
## Detailed Reference
|
||||||
|
For more details, diagrams, and code examples: [Abstract Factory Pattern - RefactoringGuru](https://refactoringguru.cn/design-patterns/abstract-factory)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Adapter Pattern
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Convert the interface of a class into another interface that clients expect.
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
Load this skill when you need to solve design problems described by this pattern in your code.
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
1. Analyze whether current code structure matches the pattern's intent.
|
||||||
|
2. Refactor or implement following the pattern's typical structure.
|
||||||
|
3. Ensure the pattern introduction reduces coupling or improves flexibility.
|
||||||
|
|
||||||
|
## Detailed Reference
|
||||||
|
For more details, diagrams, and code examples: [Adapter Pattern - RefactoringGuru](https://refactoringguru.cn/design-patterns/adapter)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Bridge Pattern
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Decouple an abstraction from its implementation so that the two can vary independently.
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
Load this skill when you need to solve design problems described by this pattern in your code.
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
1. Analyze whether current code structure matches the pattern's intent.
|
||||||
|
2. Refactor or implement following the pattern's typical structure.
|
||||||
|
3. Ensure the pattern introduction reduces coupling or improves flexibility.
|
||||||
|
|
||||||
|
## Detailed Reference
|
||||||
|
For more details, diagrams, and code examples: [Bridge Pattern - RefactoringGuru](https://refactoringguru.cn/design-patterns/bridge)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Builder Pattern
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Separate the construction of a complex object from its representation, allowing the same construction process to create different representations.
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
Load this skill when you need to solve design problems described by this pattern in your code.
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
1. Analyze whether current code structure matches the pattern's intent.
|
||||||
|
2. Refactor or implement following the pattern's typical structure.
|
||||||
|
3. Ensure the pattern introduction reduces coupling or improves flexibility.
|
||||||
|
|
||||||
|
## Detailed Reference
|
||||||
|
For more details, diagrams, and code examples: [Builder Pattern - RefactoringGuru](https://refactoringguru.cn/design-patterns/builder)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Chain of Responsibility Pattern
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Allow multiple objects to handle the same request.
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
Load this skill when you need to solve design problems described by this pattern in your code.
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
1. Analyze whether current code structure matches the pattern's intent.
|
||||||
|
2. Refactor or implement following the pattern's typical structure.
|
||||||
|
3. Ensure the pattern introduction reduces coupling or improves flexibility.
|
||||||
|
|
||||||
|
## Detailed Reference
|
||||||
|
For more details, diagrams, and code examples: [Chain of Responsibility Pattern - RefactoringGuru](https://refactoringguru.cn/design-patterns/chain-of-responsibility)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Command Pattern
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Encapsulate a request as an object, thereby allowing parameterization of clients with different requests.
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
Load this skill when you need to solve design problems described by this pattern in your code.
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
1. Analyze whether current code structure matches the pattern's intent.
|
||||||
|
2. Refactor or implement following the pattern's typical structure.
|
||||||
|
3. Ensure the pattern introduction reduces coupling or improves flexibility.
|
||||||
|
|
||||||
|
## Detailed Reference
|
||||||
|
For more details, diagrams, and code examples: [Command Pattern - RefactoringGuru](https://refactoringguru.cn/design-patterns/command)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Composite Pattern
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Compose objects into tree structures to represent part-whole hierarchies.
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
Load this skill when you need to solve design problems described by this pattern in your code.
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
1. Analyze whether current code structure matches the pattern's intent.
|
||||||
|
2. Refactor or implement following the pattern's typical structure.
|
||||||
|
3. Ensure the pattern introduction reduces coupling or improves flexibility.
|
||||||
|
|
||||||
|
## Detailed Reference
|
||||||
|
For more details, diagrams, and code examples: [Composite Pattern - RefactoringGuru](https://refactoringguru.cn/design-patterns/composite)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Decorator Pattern
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Dynamically add additional responsibilities to an object.
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
Load this skill when you need to solve design problems described by this pattern in your code.
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
1. Analyze whether current code structure matches the pattern's intent.
|
||||||
|
2. Refactor or implement following the pattern's typical structure.
|
||||||
|
3. Ensure the pattern introduction reduces coupling or improves flexibility.
|
||||||
|
|
||||||
|
## Detailed Reference
|
||||||
|
For more details, diagrams, and code examples: [Decorator Pattern - RefactoringGuru](https://refactoringguru.cn/design-patterns/decorator)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Facade Pattern
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Provide a unified interface to a set of interfaces in a subsystem, hiding the implementation details of the subsystem.
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
Load this skill when you need to solve design problems described by this pattern in your code.
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
1. Analyze whether current code structure matches the pattern's intent.
|
||||||
|
2. Refactor or implement following the pattern's typical structure.
|
||||||
|
3. Ensure the pattern introduction reduces coupling or improves flexibility.
|
||||||
|
|
||||||
|
## Detailed Reference
|
||||||
|
For more details, diagrams, and code examples: [Facade Pattern - RefactoringGuru](https://refactoringguru.cn/design-patterns/facade)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Factory Method Pattern
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Define an interface for creating an object, but let subclasses decide which class to instantiate.
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
Load this skill when you need to solve design problems described by this pattern in your code.
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
1. Analyze whether current code structure matches the pattern's intent.
|
||||||
|
2. Refactor or implement following the pattern's typical structure.
|
||||||
|
3. Ensure the pattern introduction reduces coupling or improves flexibility.
|
||||||
|
|
||||||
|
## Detailed Reference
|
||||||
|
For more details, diagrams, and code examples: [Factory Method Pattern - RefactoringGuru](https://refactoringguru.cn/design-patterns/factory-method)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Flyweight Pattern
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Use sharing to support large quantities of fine-grained objects efficiently.
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
Load this skill when you need to solve design problems described by this pattern in your code.
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
1. Analyze whether current code structure matches the pattern's intent.
|
||||||
|
2. Refactor or implement following the pattern's typical structure.
|
||||||
|
3. Ensure the pattern introduction reduces coupling or improves flexibility.
|
||||||
|
|
||||||
|
## Detailed Reference
|
||||||
|
For more details, diagrams, and code examples: [Flyweight Pattern - RefactoringGuru](https://refactoringguru.cn/design-patterns/flyweight)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Iterator Pattern
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Provide a method to sequentially access elements of an aggregate object without exposing its internal representation.
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
Load this skill when you need to solve design problems described by this pattern in your code.
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
1. Analyze whether current code structure matches the pattern's intent.
|
||||||
|
2. Refactor or implement following the pattern's typical structure.
|
||||||
|
3. Ensure the pattern introduction reduces coupling or improves flexibility.
|
||||||
|
|
||||||
|
## Detailed Reference
|
||||||
|
For more details, diagrams, and code examples: [Iterator Pattern - RefactoringGuru](https://refactoringguru.cn/design-patterns/iterator)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Mediator Pattern
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Use a mediator object to encapsulate a series of class interactions.
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
Load this skill when you need to solve design problems described by this pattern in your code.
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
1. Analyze whether current code structure matches the pattern's intent.
|
||||||
|
2. Refactor or implement following the pattern's typical structure.
|
||||||
|
3. Ensure the pattern introduction reduces coupling or improves flexibility.
|
||||||
|
|
||||||
|
## Detailed Reference
|
||||||
|
For more details, diagrams, and code examples: [Mediator Pattern - RefactoringGuru](https://refactoringguru.cn/design-patterns/mediator)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Memento Pattern
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Capture an object's internal state without violating encapsulation, so that the object can be restored to this state later.
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
Load this skill when you need to solve design problems described by this pattern in your code.
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
1. Analyze whether current code structure matches the pattern's intent.
|
||||||
|
2. Refactor or implement following the pattern's typical structure.
|
||||||
|
3. Ensure the pattern introduction reduces coupling or improves flexibility.
|
||||||
|
|
||||||
|
## Detailed Reference
|
||||||
|
For more details, diagrams, and code examples: [Memento Pattern - RefactoringGuru](https://refactoringguru.cn/design-patterns/memento)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Observer Pattern
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Define a one-to-many dependency relationship so that when one object's state changes, all dependent objects are notified and automatically updated.
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
Load this skill when you need to solve design problems described by this pattern in your code.
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
1. Analyze whether current code structure matches the pattern's intent.
|
||||||
|
2. Refactor or implement following the pattern's typical structure.
|
||||||
|
3. Ensure the pattern introduction reduces coupling or improves flexibility.
|
||||||
|
|
||||||
|
## Detailed Reference
|
||||||
|
For more details, diagrams, and code examples: [Observer Pattern - RefactoringGuru](https://refactoringguru.cn/design-patterns/observer)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Prototype Pattern
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Use a prototype instance to create other instances.
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
Load this skill when you need to solve design problems described by this pattern in your code.
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
1. Analyze whether current code structure matches the pattern's intent.
|
||||||
|
2. Refactor or implement following the pattern's typical structure.
|
||||||
|
3. Ensure the pattern introduction reduces coupling or improves flexibility.
|
||||||
|
|
||||||
|
## Detailed Reference
|
||||||
|
For more details, diagrams, and code examples: [Prototype Pattern - RefactoringGuru](https://refactoringguru.cn/design-patterns/prototype)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Proxy Pattern
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Provide a surrogate or placeholder for another object to control access to it.
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
Load this skill when you need to solve design problems described by this pattern in your code.
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
1. Analyze whether current code structure matches the pattern's intent.
|
||||||
|
2. Refactor or implement following the pattern's typical structure.
|
||||||
|
3. Ensure the pattern introduction reduces coupling or improves flexibility.
|
||||||
|
|
||||||
|
## Detailed Reference
|
||||||
|
For more details, diagrams, and code examples: [Proxy Pattern - RefactoringGuru](https://refactoringguru.cn/design-patterns/proxy)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Singleton Pattern
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Ensure a class has only one instance and provide a global point of access to it.
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
Load this skill when you need to solve design problems described by this pattern in your code.
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
1. Analyze whether current code structure matches the pattern's intent.
|
||||||
|
2. Refactor or implement following the pattern's typical structure.
|
||||||
|
3. Ensure the pattern introduction reduces coupling or improves flexibility.
|
||||||
|
|
||||||
|
## Detailed Reference
|
||||||
|
For more details, diagrams, and code examples: [Singleton Pattern - RefactoringGuru](https://refactoringguru.cn/design-patterns/singleton)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# State Pattern
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Allow an object to alter its behavior when its internal state changes.
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
Load this skill when you need to solve design problems described by this pattern in your code.
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
1. Analyze whether current code structure matches the pattern's intent.
|
||||||
|
2. Refactor or implement following the pattern's typical structure.
|
||||||
|
3. Ensure the pattern introduction reduces coupling or improves flexibility.
|
||||||
|
|
||||||
|
## Detailed Reference
|
||||||
|
For more details, diagrams, and code examples: [State Pattern - RefactoringGuru](https://refactoringguru.cn/design-patterns/state)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Strategy Pattern
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Define a family of algorithms, encapsulate each one, and make them interchangeable.
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
Load this skill when you need to solve design problems described by this pattern in your code.
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
1. Analyze whether current code structure matches the pattern's intent.
|
||||||
|
2. Refactor or implement following the pattern's typical structure.
|
||||||
|
3. Ensure the pattern introduction reduces coupling or improves flexibility.
|
||||||
|
|
||||||
|
## Detailed Reference
|
||||||
|
For more details, diagrams, and code examples: [Strategy Pattern - RefactoringGuru](https://refactoringguru.cn/design-patterns/strategy)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Template Method Pattern
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Define the skeleton of an operation, deferring some steps to subclasses.
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
Load this skill when you need to solve design problems described by this pattern in your code.
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
1. Analyze whether current code structure matches the pattern's intent.
|
||||||
|
2. Refactor or implement following the pattern's typical structure.
|
||||||
|
3. Ensure the pattern introduction reduces coupling or improves flexibility.
|
||||||
|
|
||||||
|
## Detailed Reference
|
||||||
|
For more details, diagrams, and code examples: [Template Method Pattern - RefactoringGuru](https://refactoringguru.cn/design-patterns/template-method)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Visitor Pattern
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Without changing the element classes, define new operations that act on these elements.
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
Load this skill when you need to solve design problems described by this pattern in your code.
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
1. Analyze whether current code structure matches the pattern's intent.
|
||||||
|
2. Refactor or implement following the pattern's typical structure.
|
||||||
|
3. Ensure the pattern introduction reduces coupling or improves flexibility.
|
||||||
|
|
||||||
|
## Detailed Reference
|
||||||
|
For more details, diagrams, and code examples: [Visitor Pattern - RefactoringGuru](https://refactoringguru.cn/design-patterns/visitor)
|
||||||
|
|
@ -0,0 +1,812 @@
|
||||||
|
---
|
||||||
|
name: go-backend-dev
|
||||||
|
description: "Backend Agent uses this skill to implement Golang backend. Based on implementation plan and API spec, use Domain-Driven + go-zero style architecture and TDD process to produce production-ready code. Trigger: After Task Breakdown complete (Stage 9)."
|
||||||
|
---
|
||||||
|
|
||||||
|
# /go-backend-dev — Golang Backend Implementation
|
||||||
|
|
||||||
|
Backend Agent uses this skill to implement Golang backend.
|
||||||
|
|
||||||
|
## Responsibilities
|
||||||
|
|
||||||
|
1. Establish project structure based on implementation plan (Domain-Driven + go-zero style)
|
||||||
|
2. Implement features using TDD process (Red-Green-Refactor)
|
||||||
|
3. Deliver incrementally by vertical slices (end-to-end, not layer-by-layer)
|
||||||
|
4. Implement Domain / Usecase / Logic / Repository layers
|
||||||
|
5. Write unit tests and integration tests
|
||||||
|
|
||||||
|
## Input
|
||||||
|
|
||||||
|
- Implementation plan (`./plans/{feature}.md`)
|
||||||
|
- API spec (`docs/api/{date}-{feature}.yaml`)
|
||||||
|
- DB Schema (`docs/db/{date}-{feature}.sql`)
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
- Golang code structure
|
||||||
|
- Test code (unit tests >= 80%, business logic >= 90%)
|
||||||
|
- Protobuf definitions (if gRPC needed)
|
||||||
|
|
||||||
|
## Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
Read implementation plan + API spec + DB Schema
|
||||||
|
↓
|
||||||
|
Identify vertical slices (each slice = end-to-end feature)
|
||||||
|
↓
|
||||||
|
Execute TDD loop for each slice:
|
||||||
|
├── RED: Write test → Test fails
|
||||||
|
├── GREEN: Write minimal code → Test passes
|
||||||
|
└── REFACTOR: Refactor → Test still passes
|
||||||
|
↓
|
||||||
|
Build order within slice:
|
||||||
|
domain (entity/value object/interface)
|
||||||
|
→ pkg/domain/usecase (interface)
|
||||||
|
→ pkg/domain/repository (interface)
|
||||||
|
→ pkg/usecase (implementation)
|
||||||
|
→ pkg/mock (mock)
|
||||||
|
→ internal/logic (handler logic)
|
||||||
|
→ pkg/repository (infrastructure implementation)
|
||||||
|
↓
|
||||||
|
All slices complete → Run integration tests
|
||||||
|
↓
|
||||||
|
Confirm deliverables checklist
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step Details
|
||||||
|
|
||||||
|
**1. Read Input**
|
||||||
|
|
||||||
|
Read three documents simultaneously:
|
||||||
|
- Implementation plan: Understand vertical slice breakdown and priority
|
||||||
|
- API spec: Understand endpoints, request/response structures
|
||||||
|
- DB Schema: Understand table structures and relationships
|
||||||
|
|
||||||
|
**2. Identify Vertical Slices**
|
||||||
|
|
||||||
|
Not horizontal slicing (layer by layer), but vertical slicing (end-to-end):
|
||||||
|
|
||||||
|
```
|
||||||
|
✅ Correct way (vertical):
|
||||||
|
Slice 1: User registration (domain.entity + domain.usecase interface + usecase implementation + logic + repository)
|
||||||
|
Slice 2: User login (same as above)
|
||||||
|
Slice 3: User list (same as above)
|
||||||
|
|
||||||
|
❌ Wrong way (horizontal):
|
||||||
|
Stage 1: All domain entities
|
||||||
|
Stage 2: All usecases
|
||||||
|
Stage 3: All logic handlers
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. TDD Loop (Each Slice)**
|
||||||
|
|
||||||
|
For each slice, follow Red-Green-Refactor:
|
||||||
|
|
||||||
|
```
|
||||||
|
RED: Write a test → Test fails
|
||||||
|
GREEN: Write minimal code to make test pass → Test passes
|
||||||
|
REFACTOR: Refactor code → Test still passes
|
||||||
|
```
|
||||||
|
|
||||||
|
Build order within slice (inside-out):
|
||||||
|
1. `pkg/domain/entity/` — Define Entity and Value Object
|
||||||
|
2. `pkg/domain/member/` — Define value objects and enums (with tests)
|
||||||
|
3. `pkg/domain/usecase/` — Define Use Case interface
|
||||||
|
4. `pkg/domain/repository/` — Define Repository interface
|
||||||
|
5. `pkg/usecase/` — Implement business logic (write tests first)
|
||||||
|
6. `pkg/mock/` — Generate mocks
|
||||||
|
7. `internal/logic/` — Handler logic
|
||||||
|
8. `pkg/repository/` — Infrastructure implementation (with DB tests)
|
||||||
|
|
||||||
|
**4. Testing**
|
||||||
|
|
||||||
|
After each slice completes, ensure:
|
||||||
|
- Unit tests pass (`pkg/usecase/*_test.go`)
|
||||||
|
- Value object tests pass (`pkg/domain/member/*_test.go`)
|
||||||
|
- Repository tests pass (`pkg/repository/*_test.go`)
|
||||||
|
- Integration tests pass (critical paths)
|
||||||
|
- Test coverage meets requirements
|
||||||
|
|
||||||
|
**5. Completion Verification**
|
||||||
|
|
||||||
|
Finally confirm all deliverables are complete.
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
project-root/
|
||||||
|
├── build/
|
||||||
|
│ └── Dockerfile # Build image
|
||||||
|
│
|
||||||
|
├── etc/
|
||||||
|
│ └── {service}.example.yaml # Example config file
|
||||||
|
│
|
||||||
|
├── generate/
|
||||||
|
│ └── protobuf/
|
||||||
|
│ └── {service}.proto # Protobuf definitions (if gRPC)
|
||||||
|
│
|
||||||
|
├── internal/ # Application layer (not exposed externally)
|
||||||
|
│ ├── config/
|
||||||
|
│ │ └── config.go # Application config
|
||||||
|
│ ├── logic/
|
||||||
|
│ │ └── {module}/
|
||||||
|
│ │ ├── create_{entity}_logic.go # One logic file per use case
|
||||||
|
│ │ ├── get_{entity}_logic.go
|
||||||
|
│ │ ├── update_{entity}_logic.go
|
||||||
|
│ │ └── ...
|
||||||
|
│ ├── server/
|
||||||
|
│ │ └── {module}/
|
||||||
|
│ │ └── {module}_server.go # Server definition (HTTP/gRPC)
|
||||||
|
│ └── svc/
|
||||||
|
│ └── service_context.go # Dependency injection container
|
||||||
|
│
|
||||||
|
├── pkg/ # Domain layer (can be exposed externally)
|
||||||
|
│ ├── domain/
|
||||||
|
│ │ ├── config/
|
||||||
|
│ │ │ └── config.go # Domain config
|
||||||
|
│ │ ├── entity/
|
||||||
|
│ │ │ ├── {entity}.go # Entity definition
|
||||||
|
│ │ │ ├── {entity}_uid_table.go # UID mapping table
|
||||||
|
│ │ │ └── auto_id.go # Auto ID generation
|
||||||
|
│ │ ├── {module}/
|
||||||
|
│ │ │ ├── {value_object}.go # Value objects and enums
|
||||||
|
│ │ │ └── {value_object}_test.go # Value object tests
|
||||||
|
│ │ ├── repository/
|
||||||
|
│ │ │ ├── {entity}.go # Repository interface
|
||||||
|
│ │ │ └── ...
|
||||||
|
│ │ ├── usecase/
|
||||||
|
│ │ │ ├── {module}.go # Use Case interface
|
||||||
|
│ │ │ └── ...
|
||||||
|
│ │ ├── errors.go # Domain sentinel errors
|
||||||
|
│ │ ├── const.go # Domain constants
|
||||||
|
│ │ └── redis.go # Redis domain definitions
|
||||||
|
│ ├── mock/
|
||||||
|
│ │ ├── repository/
|
||||||
|
│ │ │ ├── {entity}.go # Repository mock
|
||||||
|
│ │ │ └── ...
|
||||||
|
│ │ └── usecase/
|
||||||
|
│ │ └── {module}.go # Use Case mock
|
||||||
|
│ ├── repository/
|
||||||
|
│ │ ├── {entity}.go # Repository implementation
|
||||||
|
│ │ ├── {entity}_test.go # Repository tests
|
||||||
|
│ │ ├── {entity}_uid.go # UID Repository implementation
|
||||||
|
│ │ ├── {entity}_uid_test.go
|
||||||
|
│ │ ├── error.go # Repository error definitions
|
||||||
|
│ │ └── start_{db}_container_test.go # testcontainers startup
|
||||||
|
│ └── usecase/
|
||||||
|
│ ├── {module}.go # Use Case implementation
|
||||||
|
│ ├── {operation}.go # Specific operation
|
||||||
|
│ ├── {operation}_test.go # Use Case tests
|
||||||
|
│ └── {utils}.go # Utility functions
|
||||||
|
│
|
||||||
|
├── {service}.go # Application entry point
|
||||||
|
├── Makefile
|
||||||
|
├── go.mod
|
||||||
|
├── go.sum
|
||||||
|
├── docker-compose.yml
|
||||||
|
└── readme.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dependency Direction Rules
|
||||||
|
|
||||||
|
```
|
||||||
|
pkg/domain/ ← No external dependencies (innermost, pure definitions)
|
||||||
|
↑
|
||||||
|
pkg/domain/usecase/ ← Use Case interface (only interface definitions)
|
||||||
|
pkg/domain/repository/ ← Repository interface (only interface definitions)
|
||||||
|
↑
|
||||||
|
pkg/usecase/ ← Depends on domain interfaces (business logic implementation)
|
||||||
|
pkg/mock/ ← Depends on domain interfaces (test mocks)
|
||||||
|
↑
|
||||||
|
internal/logic/ ← Depends on usecase implementations (handler logic)
|
||||||
|
internal/server/ ← Depends on logic (HTTP/gRPC server)
|
||||||
|
internal/svc/ ← Depends on all (DI container, assemble dependencies)
|
||||||
|
↑
|
||||||
|
pkg/repository/ ← Depends on domain interfaces (infrastructure implementation)
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────┐
|
||||||
|
│ pkg/domain/ │ ← Pure definitions, no dependencies
|
||||||
|
│ ├── entity/ │
|
||||||
|
│ ├── {module}/ (value objects)│
|
||||||
|
│ ├── repository/ (interfaces) │
|
||||||
|
│ ├── usecase/ (interfaces) │
|
||||||
|
│ ├── errors.go │
|
||||||
|
│ └── const.go │
|
||||||
|
├─────────────────────────────────┤
|
||||||
|
│ pkg/usecase/ │ ← Depends on domain interfaces
|
||||||
|
│ pkg/mock/ │ ← Depends on domain interfaces
|
||||||
|
├─────────────────────────────────┤
|
||||||
|
│ internal/logic/ │ ← Depends on usecase
|
||||||
|
│ internal/server/ │
|
||||||
|
│ internal/config/ │
|
||||||
|
│ internal/svc/ │ ← DI container
|
||||||
|
├─────────────────────────────────┤
|
||||||
|
│ pkg/repository/ │ ← Depends on domain interfaces
|
||||||
|
└─────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture Principles
|
||||||
|
|
||||||
|
### `pkg/domain/` — Pure Domain Definitions
|
||||||
|
|
||||||
|
`pkg/domain/` is the core, containing only **interfaces and definitions**, no implementations:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// pkg/domain/entity/user.go — Entity definition
|
||||||
|
package entity
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
// pkg/domain/member/status.go — Value object (with tests)
|
||||||
|
package member
|
||||||
|
|
||||||
|
type Status string
|
||||||
|
|
||||||
|
const (
|
||||||
|
StatusActive Status = "active"
|
||||||
|
StatusInactive Status = "inactive"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s Status) IsValid() bool {
|
||||||
|
switch s {
|
||||||
|
case StatusActive, StatusInactive:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStatus(s string) (Status, error) {
|
||||||
|
status := Status(s)
|
||||||
|
if !status.IsValid() {
|
||||||
|
return "", fmt.Errorf("invalid status: %s", s)
|
||||||
|
}
|
||||||
|
return status, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
// pkg/domain/member/status_test.go — Value object tests
|
||||||
|
package member
|
||||||
|
|
||||||
|
func TestStatus_IsValid(t *testing.T) {
|
||||||
|
assert.True(t, StatusActive.IsValid())
|
||||||
|
assert.True(t, StatusInactive.IsValid())
|
||||||
|
assert.False(t, Status("unknown").IsValid())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewStatus(t *testing.T) {
|
||||||
|
status, err := NewStatus("active")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, StatusActive, status)
|
||||||
|
|
||||||
|
_, err = NewStatus("unknown")
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
// pkg/domain/repository/user.go — Repository interface
|
||||||
|
package repository
|
||||||
|
|
||||||
|
type UserRepository interface {
|
||||||
|
GetByID(ctx context.Context, id string) (*entity.User, error)
|
||||||
|
GetByEmail(ctx context.Context, email string) (*entity.User, error)
|
||||||
|
Create(ctx context.Context, user *entity.User) error
|
||||||
|
Update(ctx context.Context, user *entity.User) error
|
||||||
|
Delete(ctx context.Context, id string) error
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
// pkg/domain/usecase/user.go — Use Case interface
|
||||||
|
package usecase
|
||||||
|
|
||||||
|
type UserUsecase interface {
|
||||||
|
CreateUser(ctx context.Context, input CreateUserInput) (*entity.User, error)
|
||||||
|
GetUser(ctx context.Context, id string) (*entity.User, error)
|
||||||
|
UpdateUser(ctx context.Context, id string, input UpdateUserInput) (*entity.User, error)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
// pkg/domain/errors.go — Domain sentinel errors
|
||||||
|
package domain
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrUserNotFound = errors.New("user not found")
|
||||||
|
ErrInvalidInput = errors.New("invalid input")
|
||||||
|
ErrDuplicateEmail = errors.New("email already exists")
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### `pkg/usecase/` — Business Logic Implementation
|
||||||
|
|
||||||
|
One functional domain per file, test file in same directory:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// pkg/usecase/account.go — Use Case entry point
|
||||||
|
package usecase
|
||||||
|
|
||||||
|
type AccountUsecase struct {
|
||||||
|
userRepo repository.UserRepository
|
||||||
|
accountRepo repository.AccountRepository
|
||||||
|
redis *redis.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAccountUsecase(
|
||||||
|
userRepo repository.UserRepository,
|
||||||
|
accountRepo repository.AccountRepository,
|
||||||
|
redis *redis.Client,
|
||||||
|
) *AccountUsecase {
|
||||||
|
return &AccountUsecase{
|
||||||
|
userRepo: userRepo,
|
||||||
|
accountRepo: accountRepo,
|
||||||
|
redis: redis,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
// pkg/usecase/create_user.go — Single operation
|
||||||
|
package usecase
|
||||||
|
|
||||||
|
func (uc *AccountUsecase) CreateUser(ctx context.Context, input CreateUserInput) (*entity.User, error) {
|
||||||
|
if err := input.Validate(); err != nil {
|
||||||
|
return nil, fmt.Errorf("validate input: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
existing, _ := uc.userRepo.GetByEmail(ctx, input.Email)
|
||||||
|
if existing != nil {
|
||||||
|
return nil, domain.ErrDuplicateEmail
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := entity.NewUser(input.Email, input.Password, input.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("create user: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := uc.userRepo.Create(ctx, user); err != nil {
|
||||||
|
return nil, fmt.Errorf("save user: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
// pkg/usecase/create_user_test.go — Test in same directory
|
||||||
|
package usecase
|
||||||
|
|
||||||
|
func TestAccountUsecase_CreateUser_Success(t *testing.T) {
|
||||||
|
mockUserRepo := new(mock.UserRepository)
|
||||||
|
uc := NewAccountUsecase(mockUserRepo, nil, nil)
|
||||||
|
|
||||||
|
mockUserRepo.On("GetByEmail", mock.Anything, "test@example.com").Return(nil, nil)
|
||||||
|
mockUserRepo.On("Create", mock.Anything, mock.AnythingOfType("*entity.User")).Return(nil)
|
||||||
|
|
||||||
|
user, err := uc.CreateUser(context.Background(), input)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, user)
|
||||||
|
mockUserRepo.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `pkg/mock/` — Auto-Generated Mocks
|
||||||
|
|
||||||
|
```go
|
||||||
|
// pkg/mock/repository/user.go — Generated by mockery
|
||||||
|
//go:generate mockery --name=UserRepository --output=../../mock/repository --outpkg=mock_repository
|
||||||
|
|
||||||
|
package mock_repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
"your-project/pkg/domain/repository"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserRepository struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *UserRepository) GetByID(ctx context.Context, id string) (*entity.User, error) {
|
||||||
|
args := m.Called(ctx, id)
|
||||||
|
if args.Get(0) == nil {
|
||||||
|
return nil, args.Error(1)
|
||||||
|
}
|
||||||
|
return args.Get(0).(*entity.User), args.Error(1)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `internal/logic/` — Handler Logic
|
||||||
|
|
||||||
|
One logic file per use case (go-zero style):
|
||||||
|
|
||||||
|
```go
|
||||||
|
// internal/logic/account/create_user_logic.go
|
||||||
|
package account
|
||||||
|
|
||||||
|
type CreateUserLogic struct {
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCreateUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateUserLogic {
|
||||||
|
return &CreateUserLogic{
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *CreateUserLogic) CreateUser(req *types.CreateUserReq) (*types.UserResp, error) {
|
||||||
|
user, err := l.svcCtx.UserUsecase.CreateUser(l.ctx, usecase.CreateUserInput{
|
||||||
|
Email: req.Email,
|
||||||
|
Password: req.Password,
|
||||||
|
Name: req.Name,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &types.UserResp{
|
||||||
|
ID: user.ID,
|
||||||
|
Email: user.Email,
|
||||||
|
Name: user.Name,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `internal/svc/` — Dependency Injection Container
|
||||||
|
|
||||||
|
```go
|
||||||
|
// internal/svc/service_context.go
|
||||||
|
package svc
|
||||||
|
|
||||||
|
type ServiceContext struct {
|
||||||
|
Config config.Config
|
||||||
|
UserUsecase usecase.UserUsecase
|
||||||
|
AccountUsecase usecase.AccountUsecase
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServiceContext(c config.Config) *ServiceContext {
|
||||||
|
db := mongo.NewClient(c.Mongo.URI)
|
||||||
|
redisClient := redis.NewClient(c.Redis)
|
||||||
|
|
||||||
|
userRepo := repository.NewUserRepository(db)
|
||||||
|
accountRepo := repository.NewAccountRepository(db)
|
||||||
|
|
||||||
|
return &ServiceContext{
|
||||||
|
Config: c,
|
||||||
|
UserUsecase: usecase.NewUserUsecase(userRepo, redisClient),
|
||||||
|
AccountUsecase: usecase.NewAccountUsecase(userRepo, accountRepo, redisClient),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `pkg/repository/` — Infrastructure Implementation
|
||||||
|
|
||||||
|
```go
|
||||||
|
// pkg/repository/user.go
|
||||||
|
package repository
|
||||||
|
|
||||||
|
type userRepository struct {
|
||||||
|
db *mongo.Database
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserRepository(db *mongo.Database) domain.Repository.UserRepository {
|
||||||
|
return &userRepository{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *userRepository) GetByID(ctx context.Context, id string) (*entity.User, error) {
|
||||||
|
var user entity.User
|
||||||
|
err := r.db.Collection("users").FindOne(ctx, bson.M{"_id": id}).Decode(&user)
|
||||||
|
if err != nil {
|
||||||
|
if err == mongo.ErrNoDocuments {
|
||||||
|
return nil, domain.ErrUserNotFound
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("find user by id: %w", err)
|
||||||
|
}
|
||||||
|
return &user, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
// pkg/repository/user_test.go
|
||||||
|
package repository
|
||||||
|
|
||||||
|
func TestUserRepository_GetByID_Success(t *testing.T) {
|
||||||
|
db := startMongoContainer(t)
|
||||||
|
defer db.Client().Disconnect(context.Background())
|
||||||
|
|
||||||
|
repo := NewUserRepository(db)
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Coding Standards
|
||||||
|
|
||||||
|
### File Naming
|
||||||
|
|
||||||
|
```
|
||||||
|
Value objects and enums: pkg/domain/{module}/{name}.go + _test.go
|
||||||
|
Entity: pkg/domain/entity/{name}.go
|
||||||
|
Repository interface: pkg/domain/repository/{name}.go
|
||||||
|
Usecase interface: pkg/domain/usecase/{module}.go
|
||||||
|
Usecase implementation: pkg/usecase/{operation}.go + _test.go
|
||||||
|
Usecase utilities: pkg/usecase/{module}_utils.go + _test.go
|
||||||
|
Repository impl: pkg/repository/{name}.go + _test.go
|
||||||
|
Mock: pkg/mock/repository/{name}.go
|
||||||
|
pkg/mock/usecase/{module}.go
|
||||||
|
Handler logic: internal/logic/{module}/{operation}_logic.go
|
||||||
|
Server: internal/server/{module}/{module}_server.go
|
||||||
|
Service Context: internal/svc/service_context.go
|
||||||
|
Protobuf definitions: generate/protobuf/{module}.proto
|
||||||
|
Config files: etc/{service}.yaml
|
||||||
|
Dockerfile: build/Dockerfile
|
||||||
|
```
|
||||||
|
|
||||||
|
### Naming Conventions
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Package: lowercase, semantically clear
|
||||||
|
package usecase // not usecases
|
||||||
|
package repository // not repositories
|
||||||
|
package entity // not entities
|
||||||
|
|
||||||
|
// Entity struct: PascalCase, no suffix
|
||||||
|
type User struct { ... } // not UserModel, UserEntity
|
||||||
|
|
||||||
|
// Value Object: base type alias + methods
|
||||||
|
type Status string // not StatusEnum
|
||||||
|
|
||||||
|
// Interface: defined in pkg/domain/, semantic naming
|
||||||
|
type UserRepository interface { ... } // not UserRepo or UserRepositoryI
|
||||||
|
|
||||||
|
// Use Case struct: {Module}Usecase
|
||||||
|
type AccountUsecase struct { ... }
|
||||||
|
|
||||||
|
// Use Case methods: verb prefix
|
||||||
|
func (uc *AccountUsecase) CreateUser(ctx context.Context, ...) (*entity.User, error)
|
||||||
|
func (uc *AccountUsecase) GetUser(ctx context.Context, id string) (*entity.User, error)
|
||||||
|
|
||||||
|
// Logic struct: {Operation}Logic
|
||||||
|
type CreateUserLogic struct { ... }
|
||||||
|
|
||||||
|
// Error: Err prefix
|
||||||
|
var ErrUserNotFound = errors.New("user not found")
|
||||||
|
|
||||||
|
// Constant: PascalCase (exported) or camelCase (internal)
|
||||||
|
const MaxRetryCount = 3
|
||||||
|
const defaultPageSize = 20
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Sentinel errors — pkg/domain/errors.go
|
||||||
|
var (
|
||||||
|
ErrUserNotFound = errors.New("user not found")
|
||||||
|
ErrInvalidInput = errors.New("invalid input")
|
||||||
|
ErrDuplicateEmail = errors.New("email already exists")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Error wrapping — always use %w
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create user: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error checking — always use errors.Is
|
||||||
|
if errors.Is(err, domain.ErrUserNotFound) {
|
||||||
|
// handle not found
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repository errors — pkg/repository/error.go
|
||||||
|
var (
|
||||||
|
ErrMongoConnection = errors.New("mongo connection failed")
|
||||||
|
ErrRedisConnection = errors.New("redis connection failed")
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Interface Design
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Interface defined in pkg/domain/ (consumer side)
|
||||||
|
// Implementation defined in pkg/ (provider side)
|
||||||
|
|
||||||
|
// Accept interfaces, return structs
|
||||||
|
func NewUserUsecase(repo repository.UserRepository, redis *redis.Client) *UserUsecase {
|
||||||
|
return &UserUsecase{repo: repo, redis: redis}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep interfaces small (1-3 methods)
|
||||||
|
type UserRepository interface {
|
||||||
|
GetByID(ctx context.Context, id string) (*entity.User, error)
|
||||||
|
Create(ctx context.Context, user *entity.User) error
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## TDD Standards
|
||||||
|
|
||||||
|
This skill integrates with `tdd` skill, following shared TDD principles.
|
||||||
|
|
||||||
|
### Test Pyramid
|
||||||
|
|
||||||
|
```
|
||||||
|
/\
|
||||||
|
/ \
|
||||||
|
/ E2E \ <- Few critical flows
|
||||||
|
/--------\
|
||||||
|
/Integration\ <- DB + Redis (testcontainers)
|
||||||
|
/--------------\
|
||||||
|
/ Unit Tests \ <- Most, 80%+ coverage
|
||||||
|
/--------------------\
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Location
|
||||||
|
|
||||||
|
```
|
||||||
|
Test files in same directory as source:
|
||||||
|
|
||||||
|
pkg/domain/member/status_test.go ← Value object tests
|
||||||
|
pkg/usecase/create_user_test.go ← Use Case tests
|
||||||
|
pkg/repository/user_test.go ← Repository tests
|
||||||
|
pkg/repository/start_mongo_container_test.go ← testcontainers startup
|
||||||
|
```
|
||||||
|
|
||||||
|
### Vertical Slice TDD
|
||||||
|
|
||||||
|
Each slice follows this order:
|
||||||
|
|
||||||
|
```
|
||||||
|
Slice: User registration
|
||||||
|
1. RED: Write TestStatus_IsValid (value object)
|
||||||
|
2. GREEN: Write Status.IsValid()
|
||||||
|
3. RED: Write TestAccountUsecase_CreateUser_Success
|
||||||
|
4. GREEN: Write domain/entity, domain/usecase interface, pkg/usecase implementation, mock
|
||||||
|
5. RED: Write TestAccountUsecase_CreateUser_DuplicateEmail
|
||||||
|
6. GREEN: Add duplicate check
|
||||||
|
7. RED: Write TestUserRepository_Create (DB test)
|
||||||
|
8. GREEN: Write pkg/repository/user.go
|
||||||
|
9. RED: Write TestCreateUserLogic (handler test)
|
||||||
|
10. GREEN: Write internal/logic/account/create_user_logic.go
|
||||||
|
11. REFACTOR: Clean up everything
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mock Strategy
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Use mockery to auto-generate mocks
|
||||||
|
// Add go:generate directive in interface file
|
||||||
|
//go:generate mockery --name=UserRepository --output=../../mock/repository --outpkg=mock_repository
|
||||||
|
|
||||||
|
// Unit tests use mock
|
||||||
|
func TestAccountUsecase_CreateUser_Success(t *testing.T) {
|
||||||
|
mockRepo := new(mock_repository.UserRepository)
|
||||||
|
uc := usecase.NewAccountUsecase(mockRepo, nil, nil)
|
||||||
|
|
||||||
|
mockRepo.On("GetByEmail", mock.Anything, "test@example.com").Return(nil, nil)
|
||||||
|
mockRepo.On("Create", mock.Anything, mock.AnythingOfType("*entity.User")).Return(nil)
|
||||||
|
|
||||||
|
user, err := uc.CreateUser(context.Background(), input)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, user)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### testcontainers Strategy
|
||||||
|
|
||||||
|
```go
|
||||||
|
// pkg/repository/start_mongo_container_test.go
|
||||||
|
func startMongoContainer(t *testing.T) *mongo.Database {
|
||||||
|
ctx := context.Background()
|
||||||
|
req := testcontainers.ContainerRequest{
|
||||||
|
Image: "mongo:7",
|
||||||
|
ExposedPorts: []string{"27017/tcp"},
|
||||||
|
WaitingFor: wait.ForListeningPort("27017/tcp"),
|
||||||
|
}
|
||||||
|
|
||||||
|
mongoC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
|
||||||
|
ContainerRequest: req,
|
||||||
|
Started: true,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Cleanup(func() {
|
||||||
|
mongoC.Terminate(ctx)
|
||||||
|
})
|
||||||
|
|
||||||
|
// ... return connected database
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Coverage Requirements
|
||||||
|
|
||||||
|
- Value objects (`pkg/domain/member/`): >= 90%
|
||||||
|
- Use Case (`pkg/usecase/`): >= 90%
|
||||||
|
- Repository (`pkg/repository/`): >= 80%
|
||||||
|
- Logic (`internal/logic/`): >= 80% (mainly integration tests)
|
||||||
|
- Critical paths: Integration tests required
|
||||||
|
|
||||||
|
## Vertical Slice Template
|
||||||
|
|
||||||
|
File list for each vertical slice:
|
||||||
|
|
||||||
|
```
|
||||||
|
Slice: {operation}_{entity}
|
||||||
|
|
||||||
|
New/modified files:
|
||||||
|
├── pkg/domain/entity/{entity}.go ← Entity definition
|
||||||
|
├── pkg/domain/member/{value_object}.go ← Value object (if needed)
|
||||||
|
├── pkg/domain/member/{value_object}_test.go ← Value object tests
|
||||||
|
├── pkg/domain/repository/{entity}.go ← Repository interface
|
||||||
|
├── pkg/domain/usecase/{module}.go ← Use Case interface
|
||||||
|
├── pkg/usecase/{operation}.go ← Use Case implementation
|
||||||
|
├── pkg/usecase/{operation}_test.go ← Use Case tests
|
||||||
|
├── pkg/mock/repository/{entity}.go ← Repository mock
|
||||||
|
├── pkg/repository/{entity}.go ← Repository implementation
|
||||||
|
├── pkg/repository/{entity}_test.go ← Repository tests
|
||||||
|
├── internal/logic/{module}/{operation}_logic.go ← Handler logic
|
||||||
|
└── internal/svc/service_context.go ← Update DI
|
||||||
|
```
|
||||||
|
|
||||||
|
## Completion Checklist
|
||||||
|
|
||||||
|
### After Each Slice
|
||||||
|
- [ ] Value object tests pass
|
||||||
|
- [ ] Use Case tests pass
|
||||||
|
- [ ] Repository tests pass (with DB)
|
||||||
|
- [ ] Error handling complete
|
||||||
|
- [ ] Dependency direction correct (domain has no external dependencies)
|
||||||
|
|
||||||
|
### After All Complete
|
||||||
|
- [ ] Project structure follows Domain-Driven + go-zero style
|
||||||
|
- [ ] `pkg/domain/` contains all Entity, Value Object, interface definitions
|
||||||
|
- [ ] `pkg/usecase/` contains all business logic implementations
|
||||||
|
- [ ] `pkg/repository/` contains all infrastructure implementations
|
||||||
|
- [ ] `internal/logic/` contains all Handler logic
|
||||||
|
- [ ] `internal/svc/` contains complete dependency injection setup
|
||||||
|
- [ ] Unit tests >= 80% coverage
|
||||||
|
- [ ] Business logic >= 90% coverage
|
||||||
|
- [ ] Integration tests pass (critical paths)
|
||||||
|
- [ ] Error handling consistent and uses `%w` wrapping
|
||||||
|
|
||||||
|
## Related Skills
|
||||||
|
|
||||||
|
- **Prerequisite**: `prd-to-plan` (implementation plan), `be-api-design` (API spec), `dba-schema` (DB Schema)
|
||||||
|
- **辅助**: `tdd` (TDD Red-Green-Refactor process), `design-an-interface` (interface design)
|
||||||
|
- **Follow-up**: `qa` (QA testing)
|
||||||
|
|
||||||
|
## Rollback Mechanism
|
||||||
|
|
||||||
|
```
|
||||||
|
QA failed (Stage 10)
|
||||||
|
↓
|
||||||
|
Orchestrator re-assigns fix task
|
||||||
|
↓
|
||||||
|
Backend Agent fixes bug + adds regression test
|
||||||
|
↓
|
||||||
|
Re-enter QA (Stage 10)
|
||||||
|
|
||||||
|
Code Review rejected (Stage 10)
|
||||||
|
↓
|
||||||
|
Handle PR feedback
|
||||||
|
↓
|
||||||
|
Re-enter QA (Stage 10) for verification
|
||||||
|
|
||||||
|
Implementation plan not feasible
|
||||||
|
↓
|
||||||
|
Return to Task Breakdown (Stage 8) for re-decomposition
|
||||||
|
|
@ -0,0 +1,203 @@
|
||||||
|
---
|
||||||
|
name: prd-to-plan
|
||||||
|
description: "Converts PRD into a multi-phase implementation plan using vertical slices (tracer bullets). Orchestrator uses this skill at Stage 8 to produce implementation plans for Stage 9 Backend/Frontend Agent implementation. Trigger: After Design Review passes, when Orchestrator performs Task Breakdown."
|
||||||
|
---
|
||||||
|
|
||||||
|
# /prd-to-plan — PRD to Implementation Plan
|
||||||
|
|
||||||
|
Orchestrator uses this skill to convert PRD into vertical slice implementation plans.
|
||||||
|
|
||||||
|
## Responsibilities
|
||||||
|
|
||||||
|
1. Confirm PRD content is complete and in context
|
||||||
|
2. Explore existing codebase to understand architecture and patterns
|
||||||
|
3. Identify persistent architectural decisions across phases
|
||||||
|
4. Decompose PRD into vertical slices (tracer bullets)
|
||||||
|
5. Confirm slice granularity with user
|
||||||
|
6. Produce implementation plan document
|
||||||
|
|
||||||
|
## Input
|
||||||
|
|
||||||
|
- PRD document (`docs/prd/{date}-{feature}.md`)
|
||||||
|
- API specification (`docs/api/{date}-{feature}.yaml`)
|
||||||
|
- DB Schema (`docs/db/{date}-{feature}.sql`)
|
||||||
|
- Design documents (`docs/design/{date}-{feature}/`)
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
- Implementation plan: `./plans/{feature}.md`
|
||||||
|
|
||||||
|
## Process
|
||||||
|
|
||||||
|
```
|
||||||
|
Confirm PRD in context
|
||||||
|
↓
|
||||||
|
Explore codebase (architecture, patterns, integration layers)
|
||||||
|
↓
|
||||||
|
Identify persistent architectural decisions (routes, schema, models, auth)
|
||||||
|
↓
|
||||||
|
Draft vertical slice decomposition
|
||||||
|
↓
|
||||||
|
Confirm slice granularity with user
|
||||||
|
↓
|
||||||
|
Iterate until user approves
|
||||||
|
↓
|
||||||
|
Write plan file
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step Details
|
||||||
|
|
||||||
|
**1. Confirm PRD in context**
|
||||||
|
|
||||||
|
The PRD should already be in the conversation. If not, ask the user to paste or point to the file.
|
||||||
|
|
||||||
|
**2. Explore codebase**
|
||||||
|
|
||||||
|
If the codebase hasn't been explored yet, explore to understand:
|
||||||
|
- Current architectural patterns
|
||||||
|
- Existing code conventions
|
||||||
|
- Integration layers (DB, API, external services)
|
||||||
|
- Existing test patterns
|
||||||
|
|
||||||
|
**3. Identify persistent architectural decisions**
|
||||||
|
|
||||||
|
Before slicing, identify high-level decisions unlikely to change during implementation:
|
||||||
|
|
||||||
|
- Route structure / URL patterns
|
||||||
|
- Database schema shape
|
||||||
|
- Key data models
|
||||||
|
- Authentication / authorization approach
|
||||||
|
- Third-party service boundaries
|
||||||
|
|
||||||
|
Write these decisions in the plan file header for all phases to reference.
|
||||||
|
|
||||||
|
**4. Draft vertical slices**
|
||||||
|
|
||||||
|
Decompose PRD into **tracer bullet** phases. Each phase is a thin vertical slice cutting through all integration layers end-to-end, **not** horizontal slicing.
|
||||||
|
|
||||||
|
**Vertical slice principles:**
|
||||||
|
|
||||||
|
- Each slice delivers a narrow but complete path through all layers (schema, API, usecase, logic, tests)
|
||||||
|
- Completed slices can be demonstrated or verified independently
|
||||||
|
- Prefer multiple thin slices over few thick slices
|
||||||
|
- Don't include specific filenames, function names, or implementation details that may change with subsequent phases
|
||||||
|
- Include persistent decisions: route paths, schema shapes, data model names
|
||||||
|
|
||||||
|
**Horizontal slice (wrong) vs Vertical slice (correct):**
|
||||||
|
|
||||||
|
```
|
||||||
|
❌ Horizontal slice:
|
||||||
|
Phase 1: All domain entities
|
||||||
|
Phase 2: All usecases
|
||||||
|
Phase 3: All API handlers
|
||||||
|
Phase 4: All repository implementations
|
||||||
|
|
||||||
|
✅ Vertical slice (tracer bullets):
|
||||||
|
Phase 1: User registration (entity + usecase + handler + repo + tests)
|
||||||
|
Phase 2: User login (same)
|
||||||
|
Phase 3: User list (same)
|
||||||
|
Phase 4: User profile update (same)
|
||||||
|
```
|
||||||
|
|
||||||
|
**5. Confirm with user**
|
||||||
|
|
||||||
|
Present decomposition as a numbered list, each phase containing:
|
||||||
|
|
||||||
|
- **Title**: Brief descriptive name
|
||||||
|
- **User stories covered**: Which user stories from PRD this maps to
|
||||||
|
|
||||||
|
Ask the user:
|
||||||
|
- How does the granularity feel? (too coarse / too fine)
|
||||||
|
- Need to merge or split any phases?
|
||||||
|
|
||||||
|
Iterate until user approves.
|
||||||
|
|
||||||
|
**6. Write plan file**
|
||||||
|
|
||||||
|
Create `./plans/` directory (if it doesn't exist). Write Markdown file.
|
||||||
|
|
||||||
|
## Plan Template
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Plan: {Feature Name}
|
||||||
|
|
||||||
|
> Source PRD: {PRD link or identifier}
|
||||||
|
> Source API: {API specification link}
|
||||||
|
> Source DB Schema: {DB Schema link}
|
||||||
|
|
||||||
|
## Architectural Decisions
|
||||||
|
|
||||||
|
Persistent decisions across all phases:
|
||||||
|
|
||||||
|
- **Routes**: {API route structure}
|
||||||
|
- **Schema**: {database schema shape}
|
||||||
|
- **Key models**: {key data models}
|
||||||
|
- **Auth**: {authentication/authorization approach}
|
||||||
|
- **Third-party services**: {external service boundaries}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 1: {Title}
|
||||||
|
|
||||||
|
**User stories**: {list of user stories from PRD}
|
||||||
|
|
||||||
|
### What to build
|
||||||
|
|
||||||
|
End-to-end behavior description for this vertical slice. Describe complete behavior, not layer-by-layer implementation details.
|
||||||
|
|
||||||
|
### Acceptance criteria
|
||||||
|
|
||||||
|
- [ ] Acceptance criteria 1
|
||||||
|
- [ ] Acceptance criteria 2
|
||||||
|
- [ ] Acceptance criteria 3
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 2: {Title}
|
||||||
|
|
||||||
|
**User stories**: {list of user stories from PRD}
|
||||||
|
|
||||||
|
### What to build
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
### Acceptance criteria
|
||||||
|
|
||||||
|
- [ ] ...
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- Repeat for each Phase -->
|
||||||
|
```
|
||||||
|
|
||||||
|
## Role in Vibe-Kanban
|
||||||
|
|
||||||
|
```
|
||||||
|
Stage 7: Design Review passes
|
||||||
|
↓
|
||||||
|
Stage 8: Task Breakdown (Orchestrator uses prd-to-plan)
|
||||||
|
↓
|
||||||
|
Output ./plans/{feature}.md
|
||||||
|
↓
|
||||||
|
Stage 9: Implementation (Backend/Frontend Agent implements according to plan)
|
||||||
|
```
|
||||||
|
|
||||||
|
Orchestrator will parse the plan and:
|
||||||
|
1. Identify frontend and backend tasks
|
||||||
|
2. Assign to corresponding agents
|
||||||
|
3. May trigger parallel task distribution
|
||||||
|
|
||||||
|
## Dependent Skills
|
||||||
|
|
||||||
|
- **Prerequisite**: `write-a-prd` (PRD complete), `be-api-design` (API spec), `dba-schema` (DB Schema), `design-review` (design review passed)
|
||||||
|
- **Follow-up**: `go-backend-dev` (Backend implementation), frontend implementation skill
|
||||||
|
|
||||||
|
## Rollback Mechanism
|
||||||
|
|
||||||
|
```
|
||||||
|
Implementation plan infeasible
|
||||||
|
↓
|
||||||
|
Rollback to Stage 7 (Design Review) for re-review
|
||||||
|
or
|
||||||
|
Rollback to Stage 4 (API Design) to adjust design
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,296 @@
|
||||||
|
---
|
||||||
|
name: tdd
|
||||||
|
description: "Backend Agent uses this skill for Test-Driven Development. Follows Red-Green-Refactor cycle and vertical slicing principles, ensuring tests cover behavior rather than implementation details. Trigger: Implementation phase (Stage 9), integrated with go-backend-dev skill."
|
||||||
|
---
|
||||||
|
|
||||||
|
# /tdd — Test-Driven Development
|
||||||
|
|
||||||
|
Backend Agent uses this skill for Test-Driven Development.
|
||||||
|
|
||||||
|
## Core Philosophy
|
||||||
|
|
||||||
|
**Test behavior, not implementation details.**
|
||||||
|
|
||||||
|
Good tests verify behavior through public interfaces, describing the system "what" it does, not "how" it does it. Tests still pass after refactoring.
|
||||||
|
|
||||||
|
Bad tests are coupled to implementation: mocking internal collaborators, testing private methods. Tests fail after refactoring, but behavior hasn't changed.
|
||||||
|
|
||||||
|
## Anti-Pattern: Horizontal Slicing
|
||||||
|
|
||||||
|
**Don't write all tests first, then all implementations.** This is "horizontal slicing":
|
||||||
|
|
||||||
|
```
|
||||||
|
❌ Wrong way:
|
||||||
|
RED: test1, test2, test3, test4, test5
|
||||||
|
GREEN: impl1, impl2, impl3, impl4, impl5
|
||||||
|
|
||||||
|
✅ Correct way (vertical slicing):
|
||||||
|
RED→GREEN: test1 → impl1
|
||||||
|
RED→GREEN: test2 → impl2
|
||||||
|
RED→GREEN: test3 → impl3
|
||||||
|
```
|
||||||
|
|
||||||
|
Horizontal slicing produces low-quality tests:
|
||||||
|
- Tests written early verify "imagined" behavior, not "actual" behavior
|
||||||
|
- Tests become validators of data structures and function signatures, not user-observable behavior
|
||||||
|
- Tests are insensitive to real changes — they pass when behavior is broken, fail when behavior hasn't changed but after refactoring
|
||||||
|
|
||||||
|
## Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
Confirm interface changes and test scope
|
||||||
|
↓
|
||||||
|
Write first test (tracer bullet)
|
||||||
|
↓
|
||||||
|
RED: Test fails
|
||||||
|
↓
|
||||||
|
GREEN: Write minimal code to make test pass
|
||||||
|
↓
|
||||||
|
Write next test
|
||||||
|
↓
|
||||||
|
RED → GREEN loop
|
||||||
|
↓
|
||||||
|
All behavior tests complete
|
||||||
|
↓
|
||||||
|
REFACTOR: Refactor
|
||||||
|
↓
|
||||||
|
Confirm all tests still pass
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step Details
|
||||||
|
|
||||||
|
**1. Planning**
|
||||||
|
|
||||||
|
Before writing any code:
|
||||||
|
- [ ] Confirm which interface changes are needed with user
|
||||||
|
- [ ] Confirm which behaviors need testing (prioritize)
|
||||||
|
- [ ] Identify opportunities for deep modules (small interface, deep implementation)
|
||||||
|
- [ ] Design interfaces for testability
|
||||||
|
- [ ] List behaviors to test (not implementation steps)
|
||||||
|
- [ ] Get user approval for test plan
|
||||||
|
|
||||||
|
Ask: "What should the public interface look like? Which behaviors are most important to test?"
|
||||||
|
|
||||||
|
**You cannot test everything.** Confirm with user which behaviors are most important, focus testing effort on critical paths and complex logic, not every possible edge case.
|
||||||
|
|
||||||
|
**2. Tracer Bullet**
|
||||||
|
|
||||||
|
Write a test that confirms one thing about the system:
|
||||||
|
|
||||||
|
```
|
||||||
|
RED: Write first behavior test → Test fails
|
||||||
|
GREEN: Write minimal code to make test pass → Test passes
|
||||||
|
```
|
||||||
|
|
||||||
|
This is your tracer bullet — proving the end-to-end path works.
|
||||||
|
|
||||||
|
**3. Incremental Loop**
|
||||||
|
|
||||||
|
For each remaining behavior:
|
||||||
|
|
||||||
|
```
|
||||||
|
RED: Write next test → Fails
|
||||||
|
GREEN: Minimal code to make test pass → Passes
|
||||||
|
```
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
- One test at a time
|
||||||
|
- Write only enough code to make current test pass
|
||||||
|
- Don't predict future tests
|
||||||
|
- Tests focus on observable behavior
|
||||||
|
|
||||||
|
**4. Refactoring**
|
||||||
|
|
||||||
|
After all tests pass, look for refactoring candidates:
|
||||||
|
- [ ] Extract duplicate logic
|
||||||
|
- [ ] Deepen modules (move complexity behind simple interfaces)
|
||||||
|
- [ ] Apply SOLID principles naturally
|
||||||
|
- [ ] Consider what new code reveals about existing code problems
|
||||||
|
- [ ] Run tests after each refactoring step
|
||||||
|
|
||||||
|
**Never refactor while in RED state. Get back to GREEN first.**
|
||||||
|
|
||||||
|
## Good Tests vs Bad Tests
|
||||||
|
|
||||||
|
### Good Tests
|
||||||
|
|
||||||
|
**Integration style**: Test through real interfaces, not mocking internal parts.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// GOOD: Test observable behavior
|
||||||
|
func TestUserUsecase_CreateUser_Success(t *testing.T) {
|
||||||
|
mockRepo := new(mock.UserRepository)
|
||||||
|
uc := NewUserUsecase(mockRepo, logger)
|
||||||
|
|
||||||
|
mockRepo.On("GetByEmail", mock.Anything, "test@example.com").Return(nil, nil)
|
||||||
|
mockRepo.On("Create", mock.Anything, mock.AnythingOfType("*domain.User")).Return(nil)
|
||||||
|
|
||||||
|
user, err := uc.CreateUser(context.Background(), input)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, user)
|
||||||
|
assert.Equal(t, "test@example.com", user.Email)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Characteristics:
|
||||||
|
- Tests behavior that users/callers care about
|
||||||
|
- Uses only public APIs
|
||||||
|
- Tests still pass after internal implementation refactoring
|
||||||
|
- Describes "what" instead of "how"
|
||||||
|
- One logical assertion per test
|
||||||
|
|
||||||
|
### Bad Tests
|
||||||
|
|
||||||
|
**Implementation detail testing**: Coupled to internal structure.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// BAD: Test implementation details
|
||||||
|
func TestUserUsecase_CreateUser_CallsRepoCreate(t *testing.T) {
|
||||||
|
mockRepo := new(mock.UserRepository)
|
||||||
|
uc := NewUserUsecase(mockRepo, logger)
|
||||||
|
|
||||||
|
uc.CreateUser(context.Background(), input)
|
||||||
|
|
||||||
|
// This tests "how" instead of "what"
|
||||||
|
mockRepo.AssertCalled(t, "Create", mock.Anything, mock.Anything)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Red flags:
|
||||||
|
- Mocking internal collaborators just to verify they were called
|
||||||
|
- Testing private methods
|
||||||
|
- Asserting call counts or order
|
||||||
|
- Tests fail after refactoring but behavior unchanged
|
||||||
|
- Test names describe "how" instead of "what"
|
||||||
|
|
||||||
|
```go
|
||||||
|
// BAD: Bypass interface validation
|
||||||
|
func TestCreateUser_SavesToDatabase(t *testing.T) {
|
||||||
|
CreateUser(ctx, input)
|
||||||
|
row := db.QueryRow("SELECT * FROM users WHERE name = $1", "Alice")
|
||||||
|
// Direct database query, bypassing public interface
|
||||||
|
}
|
||||||
|
|
||||||
|
// GOOD: Validate through interface
|
||||||
|
func TestCreateUser_MakesUserRetrievable(t *testing.T) {
|
||||||
|
user, _ := CreateUser(ctx, input)
|
||||||
|
retrieved, _ := GetUser(ctx, user.ID)
|
||||||
|
assert.Equal(t, "Alice", retrieved.Name)
|
||||||
|
// Validate behavior through public interface
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Golang Testing Standards
|
||||||
|
|
||||||
|
### Test Naming
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Test{Unit}_{Scenario}
|
||||||
|
func TestUserUsecase_CreateUser_Success(t *testing.T) {}
|
||||||
|
func TestUserUsecase_CreateUser_InvalidEmail(t *testing.T) {}
|
||||||
|
func TestUserUsecase_CreateUser_Duplicate(t *testing.T) {}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Pyramid
|
||||||
|
|
||||||
|
```
|
||||||
|
/\
|
||||||
|
/ \
|
||||||
|
/ E2E \ <- Few critical flows
|
||||||
|
/--------\
|
||||||
|
/Integration\ <- API + DB
|
||||||
|
/--------------\
|
||||||
|
/ Unit Tests \ <- Most, 80%+ coverage
|
||||||
|
/--------------------\
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mock Strategy
|
||||||
|
|
||||||
|
Only mock at **system boundaries**:
|
||||||
|
- External APIs (payments, email, etc.)
|
||||||
|
- Database (sometimes — prefer test DB)
|
||||||
|
- Time/randomness
|
||||||
|
- File system (sometimes)
|
||||||
|
|
||||||
|
Don't mock:
|
||||||
|
- Your own classes/modules
|
||||||
|
- Internal collaborators
|
||||||
|
- Things you can control
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Use mockery to auto-generate mocks
|
||||||
|
//go:generate mockery --name=UserRepository
|
||||||
|
|
||||||
|
// Unit tests use mock repo
|
||||||
|
func TestUserUsecase_CreateUser_Success(t *testing.T) {
|
||||||
|
mockRepo := new(mock.UserRepository)
|
||||||
|
uc := NewUserUsecase(mockRepo, logger)
|
||||||
|
|
||||||
|
mockRepo.On("GetByEmail", mock.Anything, "test@example.com").Return(nil, nil)
|
||||||
|
mockRepo.On("Create", mock.Anything, mock.AnythingOfType("*domain.User")).Return(nil)
|
||||||
|
|
||||||
|
user, err := uc.CreateUser(context.Background(), input)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, user)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Interface Design for Testability
|
||||||
|
|
||||||
|
Good interfaces make testing natural:
|
||||||
|
|
||||||
|
**1. Accept dependencies, don't create them**
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Testable
|
||||||
|
func (s *UserService) CreateUser(ctx context.Context, input CreateUserInput, repo UserRepository) (*User, error) {}
|
||||||
|
|
||||||
|
// Hard to test
|
||||||
|
func (s *UserService) CreateUser(ctx context.Context, input CreateUserInput) (*User, error) {
|
||||||
|
repo := postgres.NewUserRepository(db) // Creates dependency
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Return results, don't produce side effects**
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Testable
|
||||||
|
func CalculateDiscount(cart *Cart) Discount {}
|
||||||
|
|
||||||
|
// Hard to test
|
||||||
|
func ApplyDiscount(cart *Cart) {
|
||||||
|
cart.Total -= discount // Mutates input
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Small interface surface area**
|
||||||
|
- Fewer methods = fewer tests to write
|
||||||
|
- Fewer parameters = simpler test setup
|
||||||
|
|
||||||
|
## Checklist for Each Cycle
|
||||||
|
|
||||||
|
```
|
||||||
|
[ ] Test describes behavior, not implementation
|
||||||
|
[ ] Test uses only public interfaces
|
||||||
|
[ ] Test still passes after internal refactoring
|
||||||
|
[ ] Code is minimal implementation to make current test pass
|
||||||
|
[ ] No speculative features
|
||||||
|
```
|
||||||
|
|
||||||
|
## Refactoring Candidates
|
||||||
|
|
||||||
|
After TDD cycle completes, look for:
|
||||||
|
- **Duplicate logic** → Extract function/class
|
||||||
|
- **Too long methods** → Split into private helpers (keep tests on public interface)
|
||||||
|
- **Shallow modules** → Merge or deepen
|
||||||
|
- **Feature envy** → Move logic to where the data is
|
||||||
|
- **Primitive obsession** → Introduce value objects
|
||||||
|
- **New code revealing existing code problems**
|
||||||
|
|
||||||
|
## Related Skills
|
||||||
|
|
||||||
|
- **Prerequisite**: `go-backend-dev` (used in implementation)
|
||||||
|
- **辅助**: `design-an-interface` (design interfaces for testability)
|
||||||
|
- **Follow-up**: `qa` (QA testing)
|
||||||
Loading…
Reference in New Issue