方法論概述

為什麼需要 Ticket 拆分標準

問題背景:

在大型軟體開發專案中,我們經常面臨以下挑戰:

  1. God Ticket 問題:單一任務過於龐大,包含數十個檔案修改,跨越多個架構層級
  2. 主觀判斷困境:不同開發者對「任務大小」的理解不同,導致協作效率低
  3. 進度追蹤困難:任務粒度不一致,難以準確評估完成度
  4. 風險控制失衡:大任務失敗影響大,小任務過碎增加管理成本

傳統拆分方法的問題:

  • 經驗導向:依賴個人經驗判斷,缺乏客觀標準
  • 時間估算:受個人能力和環境影響,難以標準化
  • 模糊描述:「不要太大」「保持適中」等描述無法執行
  • 後驗調整:任務執行後才發現過大,增加返工成本

核心目標

  1. 標準化任務拆分:提供統一的拆分標準,消除主觀判斷
  2. 控制任務複雜度:確保每個 Ticket 在可控範圍內
  3. 提升協作效率:明確的任務邊界,減少溝通成本
  4. 及早風險管控:設計階段就識別過大任務,降低執行風險

與其他方法論的關係

本方法論的定位:

1Ticket 設計派工方法論 (主方法論)
2├── Ticket 拆分標準方法論 ← 你正在閱讀
3│   └── 提供量化拆分標準和決策樹
4├── Code Smell 品質閘門檢測方法論
5│   └── 提供設計階段的品質檢測
6├── Ticket 生命週期管理方法論
7│   └── 提供 Ticket 執行流程管理
8└── 即時 Review 機制方法論
9    └── 提供執行過程中的 Review 機制

第一章:量化指標體系

1.1 指標 1:職責數量(Responsibilities) 最優先

定義:Ticket 需要完成的獨立職責數量。

為什麼職責是第一指標

  • 最客觀:不受個人能力影響
  • 最穩定:不受環境和參考資料影響
  • 最易溝通:PM 和工程師都能理解
  • 最精確:直接對應業務需求

職責的精確定義

什麼算一個職責

一個職責 = 一個明確可驗證的功能點或邊界條件

識別方式

  • 每個「需要實作的功能點」算一個職責
  • 每個「需要驗證的邊界條件」算一個職責
  • 每個「需要處理的錯誤情境」算一個職責

範例說明

 1範例任務:實作書籍評分功能
 2
 3職責識別:
 41. 定義 Rating Value Object(數值範圍驗證)      ← 職責 1
 52. 定義 Rating Entity(包含評分和評論)         ← 職責 2
 63. 實作 IRatingRepository 介面                  ← 職責 3
 74. 實作評分儲存邏輯                              ← 職責 4
 85. 處理無效評分錯誤                              ← 職責 5
 96. 處理資料庫錯誤                                ← 職責 6
10
11總計:6 個職責 → 超過 5 個,必須拆分

職責數量標準

等級職責數量判定說明
簡單 Ticket1 個理想單一職責,最易管理
中等 Ticket2-3 個可接受少數相關職責,可控範圍
複雜 Ticket3-5 個需檢查多職責,建議拆分
必須拆分> 5 個禁止範圍失控,必須拆分

強制規則

  • 超過 5 個職責 = 必須拆分,無例外
  • 3-5 個職責 = 評估是否可拆分,優先拆分
  • 1-2 個職責 = 理想狀態,鼓勵維持

職責識別實例

實例 1:簡單 Ticket(1 職責)
1任務:定義 SelectionManager 介面方法簽名
2
3職責分析:
4職責 1:定義 toggleSelection、clearSelection、getSelectedIds 三個方法簽名
5
6總計:1 個職責
7判定:簡單 Ticket
實例 2:中等 Ticket(2-3 職責)
1任務:實作 SelectionManager 基礎功能
2
3職責分析:
4職責 1:實作 toggleSelection 方法
5職責 2:實作 clearSelection 方法
6職責 3:實作 ChangeNotifier 通知機制
7
8總計:3 個職責
9判定:中等 Ticket(可接受)
實例 3:複雜 Ticket(3-5 職責)- 建議拆分
 1任務:實作完整的 BookRepository
 2
 3職責分析:
 4職責 1:實作 getBookByIsbn CRUD 方法
 5職責 2:實作 saveBook CRUD 方法
 6職責 3:實作 Data Mapper 轉換
 7職責 4:實作錯誤處理
 8職責 5:實作 Cache 管理
 9
10總計:5 個職責
11判定:複雜 Ticket(建議拆分為 2-3 個 Ticket)
實例 4:必須拆分(> 5 職責)
 1任務:實作書籍評分完整功能(包含 UI、UseCase、Repository)
 2
 3職責分析:
 4職責 1:定義 Rating Value Object
 5職責 2:定義 Rating Entity
 6職責 3:建立 RatingWidget UI
 7職責 4:實作 RateBookUseCase
 8職責 5:定義 IRatingRepository
 9職責 6:實作 SQLiteRatingRepository
10職責 7:處理各種異常情境
11職責 8:撰寫完整測試
12
13總計:8 個職責
14判定:God Ticket 必須拆分

拆分建議

1拆分為 4 個 Ticket:
2
3- Ticket 1: 定義 Rating Domain 模型(職責 1, 2)
4- Ticket 2: 實作 RatingRepository(職責 5, 6)
5- Ticket 3: 實作 RateBookUseCase(職責 4, 7)
6- Ticket 4: 實作 RatingWidget UI(職責 3, 8)

1.2 指標 2:程式碼行數(Lines of Code)

定義:Ticket 涉及的程式碼修改行數(新增 + 修改 + 刪除)。

為什麼需要行數指標

  • 可量化:使用 git diff --stat 精確統計
  • 可驗證:執行後可驗證預估準確性
  • 風險指標:行數越多,出錯風險越高

行數統計標準

測量方式

 1# 統計修改行數
 2git diff --stat
 3
 4# 範例輸出
 5lib/domain/entities/book.dart        | 25 +++++++++++++
 6lib/domain/repositories/i_repo.dart  | 15 ++++++--
 7test/unit/domain/book_test.dart      | 40 ++++++++++++++++++++
 83 files changed, 78 insertions(+), 2 deletions(-)
 9
10# 計算方式
11總行數 = 新增行數 + 修改行數 + 刪除行數
12      = 25 + 15 + 40 = 80

計算規則

  • 包含新增行數
  • 包含修改行數
  • 包含刪除行數
  • 不包含空行(pure whitespace changes)
  • 不包含純註解行(comment-only changes)

行數標準

等級行數範圍判定說明
簡單 Ticket< 30 行理想Interface 定義、簡單 Value Object
中等 Ticket30-70 行可接受中等實作、含測試
複雜 Ticket70-100 行需檢查複雜實作、多測試案例
必須拆分> 100 行禁止範圍過大,必須拆分

強制規則

  • 超過 100 行 = 必須拆分
  • 70-100 行 = 評估是否可拆分
  • < 70 行 = 可接受範圍

行數估算實例

實例 1:簡單 Ticket(< 30 行)
 1// 任務:定義 IBookRepository 介面
 2// lib/domain/repositories/i_book_repository.dart
 3
 4abstract class IBookRepository {
 5  /// 根據 ISBN 取得書籍
 6  Future<Book?> getBookByIsbn(String isbn);
 7
 8  /// 儲存書籍
 9  Future<void> saveBook(Book book);
10
11  /// 刪除書籍
12  Future<void> deleteBook(String isbn);
13}
14
15// 預估行數:~20 行(含註解)
16// 判定:簡單 Ticket
實例 2:中等 Ticket(30-70 行)
 1// 任務:實作 Rating Value Object
 2// lib/domain/value_objects/rating.dart + test
 3
 4// rating.dart (~40 行)
 5class Rating {
 6  final int value;
 7
 8  Rating(this.value) {
 9    if (value < 1 || value > 5) {
10      throw ValidationException.invalidRating(value);
11    }
12  }
13
14  // ... 其他方法(equals, hashCode, toString)
15}
16
17// rating_test.dart (~25 行)
18void main() {
19  test('建立有效評分', () { ... });
20  test('評分過低拋出異常', () { ... });
21  test('評分過高拋出異常', () { ... });
22}
23
24// 總計:~65 行
25// 判定:中等 Ticket
實例 3:複雜 Ticket(70-100 行)- 建議拆分
 1// 任務:實作完整的 BookRepository
 2// lib/infrastructure/repositories/sqlite_book_repository.dart + test
 3
 4// repository.dart (~80 行)
 5class SQLiteBookRepository implements IBookRepository {
 6  // ... 建構子和欄位定義
 7
 8  @override
 9  Future<Book?> getBookByIsbn(String isbn) async {
10    // ... SQL 查詢邏輯(~15 行)
11    // ... Data Mapper 轉換(~10 行)
12    // ... 錯誤處理(~5 行)
13  }
14
15  @override
16  Future<void> saveBook(Book book) async {
17    // ... SQL 插入/更新邏輯(~20 行)
18    // ... Data Mapper 轉換(~10 行)
19    // ... 錯誤處理(~5 行)
20  }
21
22  @override
23  Future<void> deleteBook(String isbn) async {
24    // ... SQL 刪除邏輯(~10 行)
25    // ... 錯誤處理(~5 行)
26  }
27}
28
29// repository_test.dart (~80 行)
30// ... 8 個測試案例
31
32// 總計:~160 行
33// 判定:超過 100 行,必須拆分

拆分建議

1拆分為 3 個 Ticket:
2
3- Ticket 1: 定義 IBookRepository 介面(~20 行)
4- Ticket 2: 實作 getBookByIsbn + 測試(~60 行)
5- Ticket 3: 實作 saveBook + deleteBook + 測試(~80 行)

1.3 指標 3:涉及檔案數(Files)

定義:Ticket 需要修改的檔案數量(不含測試檔案)。

為什麼需要檔案數指標

  • 架構層級指標:檔案數反映任務的架構範圍
  • 風險管控:修改越多檔案,影響範圍越大
  • 測試複雜度:檔案數越多,測試整合越複雜

檔案統計標準

測量方式

1# 統計修改檔案數
2git diff --name-only | grep -v '_test\.dart$' | wc -l
3
4# 範例輸出
5lib/domain/entities/book.dart
6lib/domain/repositories/i_book_repository.dart
7lib/infrastructure/repositories/sqlite_book_repository.dart
8
9# 結果:3 個檔案(不含測試)

計算規則

  • 包含新建檔案
  • 包含修改檔案
  • 包含刪除檔案
  • 不包含測試檔案(測試檔案另計為「測試數量」指標)
  • 不包含配置檔案(如 pubspec.yaml, analysis_options.yaml)

檔案數標準

等級檔案數量判定說明
簡單 Ticket1 個理想單一檔案修改
中等 Ticket2-3 個可接受相關檔案修改
複雜 Ticket3-5 個需檢查多檔案修改,建議拆分
必須拆分> 5 個禁止範圍過大,必須拆分

強制規則

  • 超過 5 個檔案 = 必須拆分
  • 3-5 個檔案 = 評估是否可拆分
  • 1-2 個檔案 = 理想狀態

檔案數實例

實例 1:簡單 Ticket(1 個檔案)
1任務:建立 Rating Value Object
2
3涉及檔案:
4lib/domain/value_objects/rating.dart
5
6總計:1 個檔案
7判定:簡單 Ticket
實例 2:中等 Ticket(2-3 個檔案)
1任務:更新 Book Entity 增加評分欄位
2
3涉及檔案:
4lib/domain/entities/book.dart         (修改)
5lib/domain/value_objects/rating.dart  (新增)
6
7總計:2 個檔案
8判定:中等 Ticket
實例 3:複雜 Ticket(3-5 個檔案)- 建議拆分
 1任務:實作完整的書籍評分功能
 2
 3涉及檔案:
 4lib/domain/entities/rating.dart
 5lib/domain/entities/book.dart
 6lib/domain/repositories/i_rating_repository.dart
 7lib/infrastructure/repositories/sqlite_rating_repository.dart
 8lib/infrastructure/mappers/rating_mapper.dart
 9
10總計:5 個檔案
11判定:複雜 Ticket(建議拆分)
實例 4:必須拆分(> 5 個檔案)
 1任務:實作評分功能(含 UI、UseCase、Repository)
 2
 3涉及檔案:
 4lib/presentation/widgets/rating_widget.dart
 5lib/presentation/controllers/rating_controller.dart
 6lib/application/use_cases/rate_book_use_case.dart
 7lib/domain/entities/rating.dart
 8lib/domain/entities/book.dart
 9lib/domain/repositories/i_rating_repository.dart
10lib/infrastructure/repositories/sqlite_rating_repository.dart
11lib/infrastructure/mappers/rating_mapper.dart
12
13總計:8 個檔案
14判定:God Ticket,必須拆分

拆分建議

1按 Clean Architecture 分層拆分為 4 個 Ticket:
2
3- Ticket 1: Domain 層(檔案 4, 5, 6)             - 3 個檔案
4- Ticket 2: Infrastructure 層(檔案 7, 8)        - 2 個檔案
5- Ticket 3: Application 層(檔案 3)              - 1 個檔案
6- Ticket 4: Presentation 層(檔案 1, 2)          - 2 個檔案

1.4 指標 4:測試用例數(Tests)

定義:Ticket 對應的測試用例數量。

為什麼需要測試數量指標

  • 品質保證指標:測試數量反映功能複雜度
  • 執行時間預估:測試數量影響 TDD 循環時間
  • 維護成本:過多測試增加維護負擔

測試統計標準

測量方式

 1// 計算測試方法數
 2void main() {
 3  group('Rating Value Object', () {
 4    test('建立有效評分', () { ... });           // 測試 1
 5    test('評分過低拋出異常', () { ... });        // 測試 2
 6    test('評分過高拋出異常', () { ... });        // 測試 3
 7    test('相同評分視為相等', () { ... });        // 測試 4
 8  });
 9}
10
11// 總計:4 個測試

計算規則

  • 每個 test('...', () {...}) 算一個測試
  • 每個 testWidgets('...', () {...}) 算一個測試
  • 包含單元測試和整合測試
  • 不包含 group() 本身(只是測試組織)

測試數量標準

等級測試數量判定說明
簡單 Ticket1-3 個理想基本功能測試
中等 Ticket3-6 個可接受含邊界和異常測試
複雜 Ticket6-10 個需檢查複雜邏輯,多測試案例
必須拆分> 10 個禁止測試過多,必須拆分

強制規則

  • 超過 10 個測試 = 必須拆分
  • 6-10 個測試 = 評估是否可拆分
  • 1-6 個測試 = 可接受範圍

測試數量實例

實例 1:簡單 Ticket(1-3 個測試)
 1// 任務:定義 Rating Value Object
 2
 3void main() {
 4  group('Rating Value Object', () {
 5    test('建立有效評分', () {
 6      final rating = Rating(4);
 7      expect(rating.value, 4);
 8    });
 9
10    test('評分過低拋出異常', () {
11      expect(() => Rating(0), throwsA(isA<ValidationException>()));
12    });
13
14    test('評分過高拋出異常', () {
15      expect(() => Rating(6), throwsA(isA<ValidationException>()));
16    });
17  });
18}
19
20// 總計:3 個測試
21// 判定:簡單 Ticket
實例 2:中等 Ticket(3-6 個測試)
 1// 任務:實作 BookRepository.getBookByIsbn
 2
 3void main() {
 4  group('BookRepository.getBookByIsbn', () {
 5    test('成功取得存在的書籍', () { ... });          // 測試 1
 6    test('書籍不存在回傳 null', () { ... });          // 測試 2
 7    test('無效 ISBN 拋出 ValidationException', () { ... }); // 測試 3
 8    test('資料庫錯誤拋出 StorageException', () { ... });    // 測試 4
 9    test('資料庫連線失敗拋出 NetworkException', () { ... }); // 測試 5
10  });
11}
12
13// 總計:5 個測試
14// 判定:中等 Ticket
實例 3:複雜 Ticket(6-10 個測試)- 建議拆分
 1// 任務:實作完整的 BookRepository CRUD
 2
 3void main() {
 4  group('BookRepository CRUD', () {
 5    // getBookByIsbn 測試
 6    test('取得存在的書籍', () { ... });              // 測試 1
 7    test('書籍不存在回傳 null', () { ... });         // 測試 2
 8
 9    // saveBook 測試
10    test('新增書籍成功', () { ... });                // 測試 3
11    test('更新書籍成功', () { ... });                // 測試 4
12    test('儲存時資料庫錯誤', () { ... });            // 測試 5
13
14    // deleteBook 測試
15    test('刪除存在的書籍', () { ... });              // 測試 6
16    test('刪除不存在的書籍', () { ... });            // 測試 7
17    test('刪除時資料庫錯誤', () { ... });            // 測試 8
18
19    // Data Mapper 測試
20    test('Entity 轉 DTO 正確', () { ... });          // 測試 9
21    test('DTO 轉 Entity 正確', () { ... });          // 測試 10
22  });
23}
24
25// 總計:10 個測試
26// 判定:複雜 Ticket(達上限,建議拆分)

拆分建議

1拆分為 3 個 Ticket:
2
3- Ticket 1: getBookByIsbn + 測試(2 個測試)    
4- Ticket 2: saveBook + 測試(3 個測試)         
5- Ticket 3: deleteBook + Mapper + 測試(5 個測試)

1.5 指標整合評估方法

整合評估原則

最高等級原則

  • 取 4 個指標中「最高的複雜度等級」作為最終評估結果
  • 任一指標達到「必須拆分」,則整個 Ticket 必須拆分

評估公式

1Ticket 複雜度 = max(職責複雜度, 行數複雜度, 檔案數複雜度, 測試數複雜度)
2
3where 複雜度等級:
4  簡單 = 1
5  中等 = 2
6  複雜 = 3
7  必須拆分 = 4

整合評估實例

實例 1:所有指標都是簡單 → 簡單 Ticket
 1任務:定義 Rating Value Object
 2
 3指標評估:
 4
 5- 職責數量:1 個職責 → 簡單
 6- 程式碼行數:25 行 → 簡單
 7- 涉及檔案:1 個檔案 → 簡單
 8- 測試用例:3 個測試 → 簡單
 9
10最終判定:簡單 Ticket(最理想狀態)
實例 2:有一個指標是中等 → 中等 Ticket
 1任務:實作 Rating Value Object
 2
 3指標評估:
 4
 5- 職責數量:2 個職責(建立、驗證)→ 中等
 6- 程式碼行數:45 行 → 中等
 7- 涉及檔案:1 個檔案 → 簡單
 8- 測試用例:5 個測試 → 中等
 9
10最終判定:中等 Ticket(可接受)
實例 3:有一個指標是複雜 → 複雜 Ticket(建議拆分)
 1任務:實作 BookRepository.getBookByIsbn
 2
 3指標評估:
 4
 5- 職責數量:3 個職責(查詢、轉換、異常處理)→ 複雜
 6- 程式碼行數:65 行 → 中等
 7- 涉及檔案:2 個檔案 → 中等
 8- 測試用例:6 個測試 → 複雜
 9
10最終判定:複雜 Ticket(建議拆分)
實例 4:有任一指標超標 → 必須拆分
 1任務:實作完整的書籍評分功能
 2
 3指標評估:
 4
 5- 職責數量:8 個職責 → 必須拆分
 6- 程式碼行數:180 行 → 必須拆分
 7- 涉及檔案:7 個檔案 → 必須拆分
 8- 測試用例:15 個測試 → 必須拆分
 9
10最終判定:God Ticket 必須立即拆分

評估決策流程

1步驟 1:計算 4 個指標
23步驟 2:取最高複雜度等級
45    ├─ 簡單 → 可直接建立 Ticket
6    ├─ 中等 → 可直接建立 Ticket(可選:評估是否拆分為更小 Ticket)
7    ├─ 複雜 → 強烈建議拆分(可選:無法拆分時勉強接受)
8    └─ 必須拆分 → 阻止建立,必須先拆分再重新評估

評估檢查清單

1□ 已計算 4 個指標的值
2□ 已確定每個指標的複雜度等級
3□ 已取最高複雜度等級作為最終判定
4□ 如為「必須拆分」,已執行拆分並重新評估
5□ 如為「複雜」,已評估是否可拆分為更小 Ticket

第二章:複雜度評估方法

2.1 複雜度等級定義

4 級複雜度體系

等級職責行數檔案測試描述處理方式
Level 1: 簡單1 個< 30 行1 個1-3 個單一職責,單一檔案直接建立 Ticket
Level 2: 中等2-3 個30-70 行2-3 個3-6 個少數相關職責,少數檔案直接建立 Ticket
Level 3: 複雜3-5 個70-100 行3-5 個6-10 個多職責,多檔案建議拆分
Level 4: 超標> 5 個> 100 行> 5 個> 10 個範圍失控必須拆分

複雜度特徵

Level 1: 簡單

  • 特徵: 最小可交付單元,職責明確
  • 適用: Interface 定義、單一 Value Object、單一方法實作
  • 優點: 風險低、易測試、易 Review
  • 預估時間: 5-20 分鐘
Level 2: 中等
  • 特徵: 少數相關職責,內聚性高
  • 適用: 含業務邏輯的 Entity、基礎 Repository 方法
  • 注意: 確保職責相關性,避免職責分散
  • 預估時間: 20-40 分鐘
Level 3: 複雜
  • 特徵: 多職責或跨檔案,整合性高
  • 適用: 完整 UseCase、Repository CRUD、複雜業務邏輯
  • 風險: 測試複雜、Review 困難、易出錯
  • 預估時間: 40-60 分鐘
  • 建議: 優先評估是否可拆分為 Level 1-2
Level 4: 超標
  • 特徵: 任一指標超標,範圍失控
  • 問題: God Ticket、高風險、難以管理
  • 處理: 必須拆分,無例外
  • 禁止: 禁止建立此等級 Ticket

2.2 評估流程

3 步驟評估流程

 1步驟 1: 初步評估(基於任務描述)
 2 3    計算 4 個指標的預估值
 4 5步驟 2: 複雜度確認(取最高等級)
 6 7    取 4 個指標中最高的複雜度等級
 8 9步驟 3: 拆分決策(基於等級決定)
1011    ├─ Level 1-2 → 可直接建立 Ticket
12    ├─ Level 3 → 評估是否可拆分
13    └─ Level 4 → 必須拆分

步驟 1:初步評估

目標: 快速估算 4 個指標的值

評估依據:

  1. 任務描述(What to do)
  2. 驗收條件(Acceptance Criteria)
  3. 預期步驟(Steps)

評估方法:

 1範例任務:實作 Book Entity
 2
 3步驟 1-1:估算職責數量
 4- 分析任務描述,列出所有需要完成的功能點
 5- 功能點 1:定義 Entity 欄位
 6- 功能點 2:實作 equals/hashCode
 7- 功能點 3:實作 toString
 8- 功能點 4:撰寫測試
 9→ 預估:4 個職責
10
11步驟 1-2:估算程式碼行數
12- 根據類似任務經驗估算
13- Entity 定義:~30 行
14- equals/hashCode:~15 行
15- toString:~5 行
16- 測試:~40 行
17→ 預估:~90 行
18
19步驟 1-3:估算檔案數量
20- 列出需要建立/修改的檔案
21- lib/domain/entities/book.dart(新增)
22- test/unit/domain/entities/book_test.dart(新增)
23→ 預估:2 個檔案(1 個生產 + 1 個測試)
24
25步驟 1-4:估算測試數量
26- 根據驗收條件估算測試案例
27- 測試 Entity 建立
28- 測試 equals(相等/不等)
29- 測試 hashCode
30- 測試 toString
31→ 預估:5 個測試

步驟 2:複雜度確認

目標: 確定最終複雜度等級

確認方法:

1對應 4 個指標到複雜度等級:
2
3職責數量:4 個 → Level 3(複雜)
4程式碼行數:90 行 → Level 3(複雜)
5檔案數量:2 個(1 生產 + 1 測試)→ Level 2(中等)
6測試數量:5 個 → Level 2(中等)
7
8取最高等級:Level 3(複雜)

確認檢查清單:

1□ 已計算所有 4 個指標
2□ 已對應每個指標到複雜度等級
3□ 已確定最高複雜度等級
4□ 已記錄評估依據

步驟 3:拆分決策

目標: 決定是否拆分以及如何拆分

決策規則:

 1Level 1-2 → 可直接建立 Ticket
 2    ├─ 無需拆分
 3    └─ 直接進入 Phase 2(測試設計)
 4
 5Level 3 → 評估是否可拆分
 6    ├─ 可拆分為 Level 1-2 → 執行拆分
 7    └─ 無法拆分 → 勉強接受,加強 Review
 8
 9Level 4 → 必須拆分
10    ├─ 阻止建立 Ticket
11    ├─ 執行拆分(使用第三章拆分策略)
12    └─ 重新評估每個拆分後的子 Ticket

Level 3 拆分評估:

 1範例:實作 Book Entity(Level 3)
 2
 3拆分評估:
 41. 是否可拆分為更小 Ticket?
 5   → 是,可拆分為兩個 Ticket
 6
 72. 如何拆分?
 8   Ticket A: 定義 Book Entity 欄位和基礎方法
 9   - 職責:定義欄位 + equals/hashCode
10   - 行數:~45 行
11   - 檔案:1 個
12   - 測試:3 個
13   → Level 2(中等)
14
15   Ticket B: 補充 Book Entity 完整功能
16   - 職責:toString + 完整測試
17   - 行數:~45 行
18   - 檔案:1 個(修改)
19   - 測試:2 個
20   → Level 1(簡單)
21
223. 拆分後依賴關係?
23   → Ticket B 依賴 Ticket A(B 在 A 完成後執行)
24
254. 拆分價值評估?
26   → 降低風險:兩個 Level 1-2 比一個 Level 3 更易管理
27   → 易於 Review:分兩次 Review,每次範圍更小
28   → 成本:增加一個 Ticket,但風險降低值得
29
30結論:建議拆分

Level 4 拆分處理:

 1範例:實作完整書籍評分功能(Level 4)
 2
 3當前狀態:
 4
 5- 職責:8 個 → Level 4
 6- 行數:180 行 → Level 4
 7- 檔案:7 個 → Level 4
 8- 測試:15 個 → Level 4
 9
10拆分決策:必須拆分(無選項)
11
12拆分方式:使用「基於 Clean Architecture 分層拆分策略」(詳見第三章)

2.3 評估決策樹

完整決策流程圖:

 1[開始評估]
 2 3[計算 4 個指標]
 4 5[取最高複雜度等級]
 6 7    ├─ Level 1(簡單)
 8    │   ↓
 9    │   [可直接建立 Ticket]
10    │   ↓
11    │   [進入 Phase 2:測試設計]
1213    ├─ Level 2(中等)
14    │   ↓
15    │   [可直接建立 Ticket]
16    │   ↓
17    │   [(可選)評估是否拆分為更小 Ticket]
18    │   ↓
19    │   [進入 Phase 2:測試設計]
2021    ├─ Level 3(複雜)
22    │   ↓
23    │   [評估是否可拆分]
24    │   ↓
25    │   ├─ 可拆分?
26    │   │   ├─ Yes → [執行拆分]
27    │   │   │           ↓
28    │   │   │       [重新評估子 Ticket]
29    │   │   │           ↓
30    │   │   │       [確保所有子 Ticket 為 Level 1-2]
31    │   │   │
32    │   │   └─ No → [勉強接受]
33    │   │              ↓
34    │   │          [標記為高風險 Ticket]
35    │   │              ↓
36    │   │          [加強 Review 機制]
37    │   │              ↓
38    │   │          [進入 Phase 2]
3940    └─ Level 4(超標)
4142        [禁止建立 Ticket]
4344        [阻止進入 Phase 2]
4546        [必須拆分(使用第三章策略)]
4748        [重新評估所有子 Ticket]
4950        [確保所有子 Ticket ≤ Level 3]

決策節點詳細說明:

節點 1:Level 3 拆分評估

 1問題:此 Ticket 是否可拆分為更小 Ticket?
 2
 3評估準則:
 41. 職責是否可分離?
 5   - 是否包含多個獨立功能點?
 6   - 是否可按照 Clean Architecture 分層拆分?
 7
 82. 拆分後是否降低複雜度?
 9   - 拆分後每個子 Ticket 是否 ≤ Level 2?
10   - 是否減少單一 Ticket 的風險?
11
123. 拆分成本是否合理?
13   - 增加的管理成本 vs 降低的風險
14   - 是否需要額外的整合測試?
15
16決策:
17
18- 滿足 1 且 2 → 建議拆分
19- 不滿足 1 或 2,但 3 成本高 → 勉強接受 Level 3
節點 2:Level 4 強制拆分
 1Level 4 無需評估,必須拆分
 2
 3拆分方法(按優先順序):
 41. 優先:按 Clean Architecture 分層拆分(詳見第三章 3.1-3.4)
 52. 次之:按職責拆分(每個職責獨立 Ticket)
 63. 最後:按檔案拆分(每個檔案獨立 Ticket)
 7
 8拆分要求:
 9
10- 所有子 Ticket 必須 ≤ Level 3
11- 建議所有子 Ticket ≤ Level 2
12- 理想所有子 Ticket = Level 1
13
14拆分後驗證:
15□ 所有子 Ticket 都已重新評估
16□ 所有子 Ticket 都 ≤ Level 3
17□ 子 Ticket 依賴關係明確
18□ 子 Ticket 總和涵蓋原始 Ticket 所有功能

2.4 複雜度評估實例

完整評估案例

案例 1:簡單 Ticket 評估

 1任務:定義 IBookRepository 介面
 2
 3步驟 1:初步評估
 4- 職責數量:1 個(定義介面方法簽名)
 5- 程式碼行數:~20 行
 6- 涉及檔案:1 個(i_book_repository.dart)
 7- 測試數量:0 個(Interface 不需測試)
 8
 9步驟 2:複雜度確認
10- 職責:1 個 → Level 1
11- 行數:20 行 → Level 1
12- 檔案:1 個 → Level 1
13- 測試:0 個 → Level 1
14→ 最高等級:Level 1
15
16步驟 3:拆分決策
17- Level 1 → 可直接建立 Ticket
18- 無需拆分
19- 進入 Phase 2

案例 2:中等 Ticket 評估

 1任務:實作 Rating Value Object
 2
 3步驟 1:初步評估
 4- 職責數量:2 個(建立 + 驗證)
 5- 程式碼行數:~50 行(含測試)
 6- 涉及檔案:1 個(rating.dart)
 7- 測試數量:5 個
 8
 9步驟 2:複雜度確認
10- 職責:2 個 → Level 2
11- 行數:50 行 → Level 2
12- 檔案:1 個 → Level 1
13- 測試:5 個 → Level 2
14→ 最高等級:Level 2
15
16步驟 3:拆分決策
17- Level 2 → 可直接建立 Ticket
18- 評估:可選拆分,但不必要(職責內聚性高)
19- 進入 Phase 2

案例 3:複雜 Ticket 評估與拆分

 1任務:實作 BookRepository CRUD
 2
 3步驟 1:初步評估
 4- 職責數量:5 個(get + save + delete + mapper + 錯誤處理)
 5- 程式碼行數:~160 行(含測試)
 6- 涉及檔案:2 個(repository.dart + mapper.dart)
 7- 測試數量:10 個
 8
 9步驟 2:複雜度確認
10- 職責:5 個 → Level 3
11- 行數:160 行 → Level 4(超過 100 行)
12- 檔案:2 個 → Level 2
13- 測試:10 個 → Level 3
14→ 最高等級:Level 4(行數超標)
15
16步驟 3:拆分決策
17- Level 4 → 必須拆分
18- 拆分方式:按 CRUD 方法拆分
19
20  Ticket A: 實作 getBookByIsbn
21  - 職責:2 個(查詢 + 轉換)
22  - 行數:~50 行
23  - 檔案:2 個
24  - 測試:3 個
25  → Level 2
26
27  Ticket B: 實作 saveBook
28  - 職責:2 個(儲存 + 錯誤處理)
29  - 行數:~60 行
30  - 檔案:1 個(修改 repository.dart)
31  - 測試:4 個
32  → Level 2
33
34  Ticket C: 實作 deleteBook
35  - 職責:2 個(刪除 + 錯誤處理)
36  - 行數:~50 行
37  - 檔案:1 個(修改 repository.dart)
38  - 測試:3 個
39  → Level 2
40
41結果:拆分為 3 個 Level 2 Ticket

案例 4:God Ticket 評估與拆分

 1任務:實作完整書籍評分功能(UI + UseCase + Repository)
 2
 3步驟 1:初步評估
 4- 職責數量:8 個(UI + Controller + UseCase + Entity + Repository + Mapper + 測試 + 整合)
 5- 程式碼行數:~300 行
 6- 涉及檔案:8 個(跨 4 個架構層級)
 7- 測試數量:20 個
 8
 9步驟 2:複雜度確認
10- 職責:8 個 → Level 4
11- 行數:300 行 → Level 4
12- 檔案:8 個 → Level 4
13- 測試:20 個 → Level 4
14→ 最高等級:Level 4(所有指標都超標)
15
16步驟 3:拆分決策
17- Level 4 → 必須拆分(God Ticket)
18- 拆分方式:使用 Clean Architecture 分層拆分策略
19
20  Ticket 1: Domain 層實作(Layer 5)
21  - Rating Entity + Rating Value Object
22  - 職責:2 個
23  - 行數:~60 行
24  - 檔案:2 個
25  - 測試:6 個
26  → Level 2
27
28  Ticket 2: Repository 層實作(Layer 4-5 Interface + Infra)
29  - IRatingRepository + SQLiteRatingRepository + Mapper
30  - 職責:3 個
31  - 行數:~90 行
32  - 檔案:3 個
33  - 測試:8 個
34  → Level 3(可接受)
35
36  Ticket 3: UseCase 層實作(Layer 3)
37  - RateBookUseCase
38  - 職責:2 個
39  - 行數:~60 行
40  - 檔案:1 個
41  - 測試:4 個
42  → Level 2
43
44  Ticket 4: Presentation 層實作(Layer 1-2)
45  - RatingWidget + RatingController
46  - 職責:2 個
47  - 行數:~90 行
48  - 檔案:2 個
49  - 測試:4 個(Widget 測試)
50  → Level 2
51
52結果:拆分為 4 個 Ticket(3 個 Level 2 + 1 個 Level 3)
53依賴順序:Ticket 1 → Ticket 2 → Ticket 3 → Ticket 4

第三章:Clean Architecture 分層拆分策略

為什麼需要基於架構分層拆分

架構分層拆分的核心價值:

  1. 單層修改原則(Single Layer Modification Principle)

    • 每個 Ticket 專注於單一架構層級
    • 降低跨層依賴帶來的複雜度
    • 提升程式碼審查效率
  2. 依賴方向一致性

    • 遵循 Clean Architecture 依賴規則(內層不依賴外層)
    • 避免循環依賴
    • 確保架構穩定性
  3. 測試可獨立性

    • 每層有明確的測試策略
    • 可獨立測試不依賴其他層
    • 簡化 Mock 和 Stub

本章內容結構:

 13.1 Clean Architecture 五層架構回顧
 2    └── 快速回顧五層架構和依賴規則
 33.2 四種標準拆分策略
 4    ├── 策略 1: Interface 定義 Ticket
 5    ├── 策略 2: 具體實作 Ticket
 6    ├── 策略 3: 測試驗證 Ticket
 7    └── 策略 4: 整合連接 Ticket
 83.3 分層拆分決策指引
 9    └── 如何選擇合適的拆分策略
103.4 分層拆分實務案例
11    └── 完整的書籍評分功能拆分範例

3.1 Clean Architecture 五層架構回顧

五層架構定義(引用自 Clean Architecture 實作方法論):

 1Layer 1 (UI - 最外層)
 2├── 職責: 使用者介面元件
 3├── 路徑: lib/presentation/widgets/, lib/presentation/pages/
 4├── 依賴: Layer 2 (Behavior)
 5└── 不依賴: Layer 3-5
 6
 7Layer 2 (Behavior)
 8├── 職責: UI 行為控制(State Management)
 9├── 路徑: lib/presentation/controllers/, lib/presentation/providers/
10├── 依賴: Layer 3 (UseCase)
11└── 不依賴: Layer 1, 4-5
12
13Layer 3 (UseCase)
14├── 職責: 業務用例協調
15├── 路徑: lib/application/use_cases/, lib/application/services/
16├── 依賴: Layer 4-5 (Domain)
17└── 不依賴: Layer 1-2
18
19Layer 4 (Domain Events/Interfaces)
20├── 職責: 領域事件和介面定義
21├── 路徑: lib/domain/events/, lib/domain/repositories/ (介面)
22├── 依賴: Layer 5 (Domain Implementation)
23└── 不依賴: Layer 1-3
24
25Layer 5 (Domain Implementation - 最內層)
26├── 職責: 領域模型實作和基礎設施
27├── 路徑: lib/domain/entities/, lib/domain/value_objects/, lib/infrastructure/
28├── 依賴: 無(核心層)
29└── 不依賴: 任何層

依賴規則(Dependency Rule):

1外層 → 內層 允許
2內層 → 外層 禁止
3
4範例:
5
6- Layer 2 (Behavior) → Layer 3 (UseCase)
7- Layer 3 (UseCase) → Layer 2 (Behavior)

單層修改原則:

  • 理想: 每個 Ticket 只修改單一層級
  • 可接受: Ticket 修改相鄰兩層(如 Interface + Implementation)
  • 禁止: Ticket 跨越超過 2 層(如 UI → Domain 直接跨越)

3.2 四種標準拆分策略

策略 1:Interface 定義 Ticket

定義: 定義一個介面及其輸入輸出契約。

適用層級: 主要用於 Layer 4 (Domain Interfaces)

職責範圍:

  • 定義 Interface 簽名
  • 定義輸入參數類型
  • 定義回傳類型
  • 撰寫文檔註解(含業務需求編號)

禁止包含:

  • 具體實作邏輯
  • 資料庫操作
  • 業務邏輯

Ticket 範本:

 1## Ticket #NNN: 定義 {Interface 名稱} 介面
 2
 3### 業務需求
 4[引用業務需求編號,如 REQ-001]
 5
 6### 目標
 7建立 `{Interface 名稱}` 介面,定義 {業務功能} 的契約
 8
 9### 步驟
101.`lib/domain/repositories/` 建立 `{interface_file}.dart`
112. 定義 `{method1}` 方法簽名
123. 定義 `{method2}` 方法簽名
134. 撰寫文檔註解(含需求編號)
14
15### 驗收條件
16- [ ] Interface 檔案建立在正確位置
17- [ ] 所有方法簽名完整且明確
18- [ ] 輸入輸出類型定義清楚
19- [ ] 包含完整的文檔註解(含需求編號)
20- [ ] dart analyze 0 錯誤

實務範例:

 1## Ticket #101: 定義 IBookRepository 介面
 2
 3### 業務需求
 4REQ-LIB-001: 書籍資料存取功能
 5
 6### 目標
 7建立 `IBookRepository` 介面,定義書籍資料存取的契約
 8
 9### 步驟
101.`lib/domain/repositories/` 建立 `i_book_repository.dart`
112. 定義 `getBookByIsbn` 方法簽名
12   ```dart
13   /// [REQ-LIB-001.1] 根據 ISBN 查詢書籍
14   ///
15   /// 參數:
16   /// - isbn: 書籍 ISBN 編號
17   ///
18   /// 回傳:
19   /// - Book 物件(存在)或 null(不存在)
20   Future<Book?> getBookByIsbn(String isbn);
  1. 定義 saveBook 方法簽名
  2. 定義 deleteBook 方法簽名
  3. 撰寫文檔註解

驗收條件

  • Interface 檔案建立在 lib/domain/repositories/
  • 3 個方法簽名完整且明確
  • 輸入輸出類型定義清楚
  • 包含完整的文檔註解(含需求編號)
  • dart analyze 0 錯誤

指標評估

  • 職責: 1 個(定義介面)
  • 行數: ~25 行
  • 檔案: 1 個
  • 測試: 0 個(Interface 不需單元測試)

→ Level 1(簡單)

 1
 2---
 3
 4#### 策略 2:具體實作 Ticket
 5
 6**定義**: 實作一個類別的核心邏輯。
 7
 8**適用層級**:
 9- Layer 5 (Domain Implementation): Entity, Value Object
10- Layer 5 (Infrastructure): Repository 實作, Service 實作
11
12**職責範圍**:
13- 實作類別邏輯
14- 實現介面方法
15- 處理異常
16- 撰寫單元測試
17
18**禁止包含**:
19- UI 元件
20- 跨層整合(如直接呼叫 UseCase)
21- 測試以外的其他層修改
22
23**Ticket 範本**:
24
25```markdown
26## Ticket #NNN: 實作 {類別名稱}
27
28### 業務需求
29[引用業務需求編號]
30
31### 目標
32實作 `{類別名稱}`,提供 {業務功能} 的具體實現
33
34### 依賴 Ticket
35- Ticket #XXX: 定義 {Interface 名稱} 介面(必須先完成)
36
37### 步驟
381. 建立 `{類別名稱}` 類別(實作 `{Interface}`)
392. 實作 `{method1}` 方法
403. 實作 `{method2}` 方法
414. 處理異常情況
425. 撰寫單元測試(正常流程 + 異常處理)
436. 確保所有測試通過
44
45### 驗收條件
46- [ ] 實作所有 Interface 方法
47- [ ] 異常處理完整
48- [ ] 單元測試 100% 通過
49- [ ] 測試覆蓋正常流程和異常處理
50- [ ] dart analyze 0 錯誤

實務範例:

 1## Ticket #102: 實作 SQLiteBookRepository
 2
 3### 業務需求
 4REQ-LIB-001: 書籍資料存取功能
 5
 6### 目標
 7實作 `SQLiteBookRepository`,提供書籍資料的 SQLite 儲存
 8
 9### 依賴 Ticket
10- Ticket #101: 定義 IBookRepository 介面(必須先完成)
11
12### 步驟
131.`lib/infrastructure/repositories/` 建立 `sqlite_book_repository.dart`
142. 實作 `getBookByIsbn` 方法
15   - SQL 查詢邏輯
16   - Data Mapper 轉換(DTO → Entity)
17   - 錯誤處理(Database Exception)
183. 實作 `saveBook` 方法
19   - SQL 插入/更新邏輯
20   - Data Mapper 轉換(Entity → DTO)
21   - 錯誤處理
224. 實作 `deleteBook` 方法
235. 撰寫單元測試
24   - 正常流程:CRUD 操作成功
25   - 異常處理:資料庫錯誤、資料不存在
266. 確保所有測試通過
27
28### 驗收條件
29- [ ] 實作所有 IBookRepository 方法
30- [ ] 異常處理完整(DatabaseException, ValidationException)
31- [ ] 單元測試 100% 通過(至少 6 個測試案例)
32- [ ] dart analyze 0 錯誤
33
34### 指標評估
35- 職責: 3 個(CRUD 實作、Mapper、異常處理)
36- 行數: ~80 行(含測試)
37- 檔案: 1 個
38- 測試: 6 個
39→ Level 2(中等)

策略 3:測試驗證 Ticket

定義: 撰寫一組相關的測試用例,補強現有實作的測試覆蓋率。

適用時機:

  • 現有實作缺乏完整測試
  • 需要補充邊界測試和異常測試
  • TDD 紅綠燈循環中的「紅燈」階段

職責範圍:

  • 撰寫單元測試
  • 覆蓋正常流程
  • 覆蓋邊界條件
  • 覆蓋異常處理
  • 確保測試通過

禁止包含:

  • 修改生產程式碼(除非是修正測試發現的 Bug)
  • 新增功能
  • 重構(測試 Ticket 專注於驗證,不做重構)

Ticket 範本:

 1## Ticket #NNN: 撰寫 {功能} 測試
 2
 3### 業務需求
 4[引用業務需求編號]
 5
 6### 目標
 7撰寫 `{類別名稱}` 的完整測試用例,確保 {業務功能} 正確性
 8
 9### 依賴 Ticket
10- Ticket #XXX: 實作 {類別名稱}(必須先完成)
11
12### 步驟
131. 建立測試檔案 `{class}_test.dart`
142. 撰寫正常流程測試
153. 撰寫邊界條件測試
164. 撰寫異常處理測試
175. 確保所有測試通過
18
19### 驗收條件
20- [ ] 測試檔案建立在正確位置
21- [ ] 至少 N 個測試用例
22- [ ] 覆蓋正常流程、邊界條件和異常處理
23- [ ] 所有測試 100% 通過

實務範例:

 1## Ticket #103: 撰寫 BookRepository 整合測試
 2
 3### 業務需求
 4REQ-LIB-001: 書籍資料存取功能
 5
 6### 目標
 7撰寫 `BookRepository` 的整合測試,驗證資料庫操作正確性
 8
 9### 依賴 Ticket
10- Ticket #102: 實作 SQLiteBookRepository(必須先完成)
11
12### 步驟
131. 建立測試檔案 `test/integration/repositories/book_repository_test.dart`
142. 撰寫 `getBookByIsbn` 測試
15   - 測試 1: 成功取得存在的書籍
16   - 測試 2: 書籍不存在回傳 null
17   - 測試 3: 無效 ISBN 拋出異常
183. 撰寫 `saveBook` 測試
19   - 測試 4: 新增書籍成功
20   - 測試 5: 更新現有書籍成功
214. 撰寫 `deleteBook` 測試
22   - 測試 6: 刪除存在的書籍
23   - 測試 7: 刪除不存在的書籍無異常
245. 確保所有測試通過
25
26### 驗收條件
27- [ ] 測試檔案建立在 `test/integration/repositories/`
28- [ ] 至少 7 個整合測試案例
29- [ ] 覆蓋正常流程和異常處理
30- [ ] 所有測試 100% 通過
31
32### 指標評估
33- 職責: 1 個(撰寫測試)
34- 行數: ~60 行(純測試)
35- 檔案: 1 個(測試檔案)
36- 測試: 7 個
37→ Level 2(中等)

策略 4:整合連接 Ticket

定義: 連接兩個模組並驗證整合,實現端到端流程。

適用時機:

  • 需要連接 Layer 3 (UseCase) 和 Layer 5 (Repository)
  • 需要連接 Layer 2 (Controller) 和 Layer 3 (UseCase)
  • 完成分層實作後的整合階段

職責範圍:

  • 連接 Use Case 和 Repository
  • 實作依賴注入
  • 撰寫整合測試
  • 驗證端到端流程

禁止包含:

  • 修改核心業務邏輯(應在具體實作 Ticket 完成)
  • 跨越超過 2 層的整合
  • UI 實作(應獨立為 Presentation 層 Ticket)

Ticket 範本:

 1## Ticket #NNN: 整合 {UseCase} 到 {Repository}
 2
 3### 業務需求
 4[引用業務需求編號]
 5
 6### 目標
 7`{Repository}` 整合到 `{UseCase}`,實現 {業務功能} 完整流程
 8
 9### 依賴 Ticket
10- Ticket #XXX: 定義 {Interface}(必須先完成)
11- Ticket #YYY: 實作 {Repository}(必須先完成)
12
13### 步驟
141. 修改 `{UseCase}` 注入 `{Interface}`
152.`execute` 方法中呼叫 Repository 方法
163. 處理 Repository 異常
174. 撰寫整合測試驗證端到端流程
185. 確保所有測試通過
19
20### 驗收條件
21- [ ] `{UseCase}` 正確注入 `{Interface}`
22- [ ] 端到端流程正常運作
23- [ ] 整合測試 100% 通過
24- [ ] 異常處理完整

實務範例:

 1## Ticket #104: 整合 BookRepository 到 GetBookUseCase
 2
 3### 業務需求
 4REQ-LIB-001: 書籍查詢功能
 5
 6### 目標
 7`BookRepository` 整合到 `GetBookUseCase`,實現完整書籍查詢流程
 8
 9### 依賴 Ticket
10- Ticket #101: 定義 IBookRepository 介面(必須先完成)
11- Ticket #102: 實作 SQLiteBookRepository(必須先完成)
12
13### 步驟
141. 修改 `GetBookInteractor` 注入 `IBookRepository`
15   ```dart
16   class GetBookInteractor implements GetBookUseCase {
17     final IBookRepository _repository;
18
19     GetBookInteractor(this._repository);
20
21     @override
22     Future<Book?> execute(String isbn) async {
23       // 呼叫 Repository
24     }
25   }
  1. execute 方法中呼叫 repository.getBookByIsbn
  2. 處理 Repository 異常(ValidationException, StorageException)
  3. 撰寫整合測試
    • 測試 1: 成功查詢書籍
    • 測試 2: 書籍不存在
    • 測試 3: Repository 異常處理
  4. 確保所有測試通過

驗收條件(整合應用案例)

  • GetBookInteractor 正確注入 IBookRepository
  • 端到端流程正常運作
  • 整合測試 100% 通過(至少 3 個測試案例)
  • 異常處理完整

指標評估(整合應用案例)

  • 職責: 2 個(注入、整合)
  • 行數: ~50 行(含測試)
  • 檔案: 1 個(修改 UseCase)
  • 測試: 3 個

→ Level 2(中等)

 1
 2---
 3
 4### 3.3 分層拆分決策指引
 5
 6**決策流程圖**:
 7
 8```text
 9[分析 Ticket 涉及的架構層級]
1011    ├─ 單層修改?
12    │   ├─ Yes → 選擇對應策略
13    │   │   ├─ Layer 4 Interface → 策略 1: Interface 定義
14    │   │   ├─ Layer 5 Implementation → 策略 2: 具體實作
15    │   │   └─ 補充測試 → 策略 3: 測試驗證
16    │   │
17    │   └─ No → 跨層修改?
18    │       ├─ 相鄰兩層整合 → 策略 4: 整合連接
19    │       └─ 跨越超過 2 層 → 必須拆分為多個 Ticket

決策準則:

準則 1: 優先單層修改

1問題: Ticket 是否只修改單一架構層級?
2
3- Yes → 選擇策略 1-3(Interface / 實作 / 測試)
4- No → 評估是否可拆分為多個單層 Ticket
準則 2: 相鄰層整合可接受
 1問題: 是否為相鄰兩層的整合?
 2
 3範例:
 4
 5- Layer 3 (UseCase) + Layer 4-5 (Repository) 可接受
 6- Layer 2 (Controller) + Layer 3 (UseCase) 可接受
 7- Layer 1 (UI) + Layer 5 (Domain) 禁止(跨越太多層)
 8
 9決策:
10
11- 相鄰兩層整合 → 策略 4: 整合連接
12- 跨越超過 2 層 → 必須拆分為多個 Ticket
準則 3: Interface 先行
1原則: Interface 定義必須先於實作
2
3範例拆分順序:
41. Ticket A: 定義 IBookRepository(策略 1)
52. Ticket B: 實作 SQLiteBookRepository(策略 2)
63. Ticket C: 整合到 GetBookUseCase(策略 4)
7
8依賴關係: Ticket C 依賴 B,B 依賴 A
準則 4: 測試獨立或整合
1問題: 測試應該獨立 Ticket 還是整合到實作 Ticket?
2
3決策:
4
5- 實作 Ticket < Level 2  整合測試到實作 Ticket
6- 實作 Ticket = Level 2-3  可選擇獨立測試 Ticket
7- 測試案例 > 10 個 → 必須獨立測試 Ticket(策略 3)

3.4 分層拆分實務案例

完整案例:書籍評分功能實作

原始 God Ticket 分析

1原始任務: 實作完整書籍評分功能(UI + UseCase + Repository)
2
3指標評估:
4- 職責: 8 個
5- 行數: ~300 行
6- 檔案: 8 個
7- 測試: 20 個
8→ Level 4(所有指標都超標)必須拆分

分層拆分方案

拆分為 6 個 Ticket(按 Clean Architecture 分層):


Ticket 1: 定義 Rating Domain 模型(Layer 5 - Domain Implementation)
 1## Ticket #201: 定義 Rating Value Object
 2
 3### 層級: Layer 5 (Domain Implementation)
 4### 策略: 策略 2(具體實作)
 5
 6### 業務需求
 7REQ-RATING-001: 書籍評分功能
 8
 9### 目標
10建立 `Rating` Value Object,封裝評分規則(1-5 分)
11
12### 步驟
131. 建立 `lib/domain/value_objects/rating.dart`
142. 實作 Rating 類別
15   - 建構子驗證(1-5 分)
16   - equals / hashCode
17   - toString
183. 撰寫單元測試(正常、邊界、異常)
194. 確保測試通過
20
21### 驗收條件
22- [ ] Rating 類別實作完整
23- [ ] 驗證邏輯正確(1-5 分)
24- [ ] 單元測試 100% 通過(至少 5 個測試)
25- [ ] dart analyze 0 錯誤
26
27### 指標評估
28- 職責: 1 個
29- 行數: ~50 行
30- 檔案: 1 個
31- 測試: 5 個
32→ Level 2(中等)

Ticket 2: 定義 Rating Entity(Layer 5 - Domain Implementation)

 1## Ticket #202: 定義 Rating Entity
 2
 3### 層級: Layer 5 (Domain Implementation)
 4### 策略: 策略 2(具體實作)
 5### 依賴: Ticket #201(Rating Value Object)
 6
 7### 業務需求
 8REQ-RATING-001: 書籍評分功能
 9
10### 目標
11建立 `Rating` Entity,包含評分和評論資訊
12
13### 步驟
141. 建立 `lib/domain/entities/rating.dart`
152. 實作 Rating Entity
16   - 欄位: ratingValue (Rating VO), comment (String), userId, bookIsbn
17   - equals / hashCode
183. 撰寫單元測試
194. 確保測試通過
20
21### 驗收條件
22- [ ] Rating Entity 實作完整
23- [ ] 使用 Rating Value Object
24- [ ] 單元測試 100% 通過(至少 3 個測試)
25- [ ] dart analyze 0 錯誤
26
27### 指標評估
28- 職責: 1 個
29- 行數: ~40 行
30- 檔案: 1 個
31- 測試: 3 個
32→ Level 1(簡單)

Ticket 3: 定義 IRatingRepository 介面(Layer 4 - Domain Interfaces)

 1## Ticket #203: 定義 IRatingRepository 介面
 2
 3### 層級: Layer 4 (Domain Interfaces)
 4### 策略: 策略 1(Interface 定義)
 5
 6### 業務需求
 7REQ-RATING-001: 書籍評分功能
 8
 9### 目標
10建立 `IRatingRepository` 介面,定義評分資料存取契約
11
12### 步驟
131. 建立 `lib/domain/repositories/i_rating_repository.dart`
142. 定義 `saveRating` 方法簽名
153. 定義 `getRatingsByBookIsbn` 方法簽名
164. 撰寫文檔註解
17
18### 驗收條件
19- [ ] Interface 檔案建立在正確位置
20- [ ] 2 個方法簽名完整
21- [ ] 文檔註解包含需求編號
22- [ ] dart analyze 0 錯誤
23
24### 指標評估
25- 職責: 1 個
26- 行數: ~20 行
27- 檔案: 1 個
28- 測試: 0 個
29→ Level 1(簡單)

Ticket 4: 實作 SQLiteRatingRepository(Layer 5 - Infrastructure)

 1## Ticket #204: 實作 SQLiteRatingRepository
 2
 3### 層級: Layer 5 (Infrastructure)
 4### 策略: 策略 2(具體實作)
 5### 依賴: Ticket #202, #203
 6
 7### 業務需求
 8REQ-RATING-001: 書籍評分功能
 9
10### 目標
11實作 `SQLiteRatingRepository`,提供評分資料的 SQLite 儲存
12
13### 步驟
141. 建立 `lib/infrastructure/repositories/sqlite_rating_repository.dart`
152. 實作 `saveRating` 方法
16   - SQL 插入邏輯
17   - Data Mapper 轉換
18   - 錯誤處理
193. 實作 `getRatingsByBookIsbn` 方法
204. 撰寫單元測試(CRUD + 異常)
215. 確保測試通過
22
23### 驗收條件
24- [ ] 實作所有 IRatingRepository 方法
25- [ ] 異常處理完整
26- [ ] 單元測試 100% 通過(至少 6 個測試)
27- [ ] dart analyze 0 錯誤
28
29### 指標評估
30- 職責: 3 個(CRUD、Mapper、異常)
31- 行數: ~90 行
32- 檔案: 1 個
33- 測試: 6 個
34→ Level 3(複雜)可接受

Ticket 5: 實作 RateBookUseCase(Layer 3 - UseCase)

 1## Ticket #205: 實作 RateBookUseCase
 2
 3### 層級: Layer 3 (UseCase)
 4### 策略: 策略 4(整合連接)
 5### 依賴: Ticket #203, #204
 6
 7### 業務需求
 8REQ-RATING-001: 書籍評分功能
 9
10### 目標
11實作 `RateBookUseCase`,協調書籍評分業務流程
12
13### 步驟
141. 建立 `lib/application/use_cases/rate_book_use_case.dart`
152. 注入 `IRatingRepository`
163. 實作 `execute` 方法
17   - 建立 Rating Entity
18   - 呼叫 Repository 儲存
19   - 處理異常
204. 撰寫整合測試
215. 確保測試通過
22
23### 驗收條件
24- [ ] UseCase 正確注入 IRatingRepository
25- [ ] 業務流程完整
26- [ ] 整合測試 100% 通過(至少 4 個測試)
27- [ ] 異常處理完整
28- [ ] dart analyze 0 錯誤
29
30### 指標評估
31- 職責: 2 個(協調、整合)
32- 行數: ~60 行
33- 檔案: 1 個
34- 測試: 4 個
35→ Level 2(中等)

Ticket 6: 實作 RatingWidget UI(Layer 1-2 - Presentation)

 1## Ticket #206: 實作 RatingWidget 和 RatingController
 2
 3### 層級: Layer 1 (UI) + Layer 2 (Behavior)
 4### 策略: 策略 2 + 策略 4(實作 + 整合)
 5### 依賴: Ticket #205
 6
 7### 業務需求
 8REQ-RATING-001: 書籍評分功能
 9
10### 目標
11實作評分 UI 元件和行為控制
12
13### 步驟
141. 建立 `lib/presentation/widgets/rating_widget.dart`
152. 實作 RatingWidget(5 星評分 UI)
163. 建立 `lib/presentation/controllers/rating_controller.dart`
174. 實作 RatingController
18   - 注入 RateBookUseCase
19   - 呼叫 UseCase 評分
20   - 狀態管理
215. 撰寫 Widget 測試
226. 確保測試通過
23
24### 驗收條件
25- [ ] RatingWidget UI 正確顯示
26- [ ] RatingController 正確整合 UseCase
27- [ ] Widget 測試 100% 通過(至少 4 個測試)
28- [ ] dart analyze 0 錯誤
29
30### 指標評估
31- 職責: 2 個(UI、Controller)
32- 行數: ~80 行
33- 檔案: 2 個
34- 測試: 4 個
35→ Level 2(中等)

拆分結果總結

拆分前後對比:

項目拆分前(God Ticket)拆分後(6 個 Ticket)
職責數量8 個平均 1.7 個
程式碼行數~300 行平均 57 行
檔案數量8 個平均 1.2 個
測試數量20 個平均 4 個
複雜度等級Level 45 個 Level 1-2
1 個 Level 3

拆分效益:

  • 風險降低: 從 1 個高風險任務 → 6 個低風險任務
  • 並行開發: 可 2-3 人同時開發不同層級
  • 易於 Review: 每個 PR 範圍小,Review 時間縮短
  • 依賴明確: 清楚的 Ticket 依賴順序
  • 測試獨立: 每層可獨立測試,Mock 簡單

執行順序:

 1Phase 1(可並行):
 2├─ Ticket #201: Rating Value Object
 3└─ Ticket #202: Rating Entity (依賴 #201)
 4
 5Phase 2(可並行):
 6├─ Ticket #203: IRatingRepository Interface
 7└─ Ticket #204: SQLiteRatingRepository (依賴 #202, #203)
 8
 9Phase 3:
10└─ Ticket #205: RateBookUseCase (依賴 #204)
11
12Phase 4:
13└─ Ticket #206: RatingWidget UI (依賴 #205)

第四章:Ticket 大小標準與範例

4.1 簡單 Ticket 標準與範例(Level 1)

Level 1 特徵

  • 職責:1 個明確職責
  • 行數:< 30 行
  • 檔案:1 個
  • 測試:1-3 個

適用場景

  • Interface 定義
  • 單一 Value Object
  • 單一方法實作
  • 簡單配置修改

範例 1:定義 IBookRepository 介面

 1## Ticket #1: 定義 IBookRepository 介面
 2
 3### 層級:Layer 4 (Domain Interfaces)
 4
 5### 業務需求
 6REQ-LIB-001: 書籍資料存取功能
 7
 8### 目標
 9建立 `IBookRepository` 介面,定義書籍資料存取的契約
10
11### 步驟
121.`lib/domain/repositories/` 建立 `i_book_repository.dart`
132. 定義 `getBookByIsbn(String isbn)` 方法簽名
143. 定義 `saveBook(Book book)` 方法簽名
154. 定義 `deleteBook(String isbn)` 方法簽名
165. 撰寫文檔註解(含需求編號)
17
18### 驗收條件
19- [ ] Interface 檔案建立在 `lib/domain/repositories/`
20- [ ] 3 個方法簽名完整且明確
21- [ ] 輸入輸出類型定義清楚
22- [ ] 文檔註解包含需求編號 REQ-LIB-001
23- [ ] dart analyze 0 錯誤
24
25### 指標評估
26- 職責數量:1 個(定義介面契約)
27- 程式碼行數:~20 行
28- 涉及檔案:1 個
29- 測試用例:0 個(Interface 不需單元測試)
30→ Level 1(簡單)
31
32### 預估時間
3310-15 分鐘

範例 2:建立 Rating Value Object

 1## Ticket #2: 建立 Rating Value Object
 2
 3### 層級:Layer 5 (Domain Implementation)
 4
 5### 業務需求
 6REQ-RATING-001: 書籍評分功能
 7
 8### 目標
 9建立 `Rating` Value Object,封裝評分規則(1-5 分)
10
11### 步驟
121.`lib/domain/value_objects/` 建立 `rating.dart`
132. 實作 Rating 類別
14   - 建構子驗證(1-5 分範圍)
15   - equals / hashCode
16   - toString
173. 撰寫單元測試
18   - 測試 1:建立有效評分
19   - 測試 2:評分過低拋出異常
20   - 測試 3:評分過高拋出異常
214. 確保所有測試通過
22
23### 驗收條件
24- [ ] Rating 類別實作完整
25- [ ] 驗證邏輯正確(1-5 分)
26- [ ] equals / hashCode / toString 實作正確
27- [ ] 單元測試 100% 通過(至少 3 個測試)
28- [ ] dart analyze 0 錯誤
29
30### 指標評估
31- 職責數量:1 個(實作 Value Object)
32- 程式碼行數:~25 行(含測試)
33- 涉及檔案:1 個
34- 測試用例:3 個
35→ Level 1(簡單)
36
37### 預估時間
3815-20 分鐘

4.2 中等 Ticket 標準與範例(Level 2)

Level 2 特徵

  • 職責:2-3 個相關職責
  • 行數:30-70 行
  • 檔案:2-3 個
  • 測試:3-6 個

適用場景

  • 含業務邏輯的 Entity
  • 基礎 Repository 方法
  • 簡單 UseCase
  • 單一功能的 Controller

範例 3:實作 Book Entity

 1## Ticket #3: 實作 Book Entity
 2
 3### 層級:Layer 5 (Domain Implementation)
 4
 5### 業務需求
 6REQ-LIB-001: 書籍資料模型
 7
 8### 目標
 9建立 `Book` Entity,封裝書籍核心資料
10
11### 步驟
121.`lib/domain/entities/` 建立 `book.dart`
132. 定義 Entity 欄位
14   - isbn (String, required)
15   - title (String, required)
16   - author (String, required)
17   - publishDate (DateTime, optional)
183. 實作 equals / hashCode(基於 isbn)
194. 實作 toString
205. 撰寫單元測試
21   - 測試 1:建立有效 Book
22   - 測試 2:equals 正確比較(相同 ISBN)
23   - 測試 3:equals 正確比較(不同 ISBN)
24   - 測試 4:hashCode 一致性
256. 確保測試通過
26
27### 驗收條件
28- [ ] Book Entity 欄位定義完整
29- [ ] equals / hashCode 實作正確(基於 isbn)
30- [ ] toString 回傳清楚的字串表示
31- [ ] 單元測試 100% 通過(至少 4 個測試)
32- [ ] dart analyze 0 錯誤
33
34### 指標評估
35- 職責數量:3 個(欄位定義、equals/hashCode、toString)
36- 程式碼行數:~50 行(含測試)
37- 涉及檔案:1 個
38- 測試用例:4 個
39→ Level 2(中等)
40
41### 預估時間
4225-35 分鐘

範例 4:實作 GetBookUseCase

 1## Ticket #4: 實作 GetBookUseCase
 2
 3### 層級:Layer 3 (UseCase)
 4
 5### 業務需求
 6REQ-LIB-001: 查詢書籍功能
 7
 8### 目標
 9實作 `GetBookUseCase`,協調書籍查詢業務流程
10
11### 依賴 Ticket
12- Ticket #1: 定義 IBookRepository 介面(必須先完成)
13
14### 步驟
151.`lib/application/use_cases/` 建立 `get_book_use_case.dart`
162. 定義 `GetBookUseCase` 介面
173. 實作 `GetBookInteractor`
18   - 注入 `IBookRepository`
19   - 實作 `execute(String isbn)` 方法
20   - 處理 Repository 異常
214. 撰寫單元測試
22   - 測試 1:成功查詢書籍
23   - 測試 2:書籍不存在回傳 null
24   - 測試 3:無效 ISBN 拋出 ValidationException
25   - 測試 4:Repository 異常正確處理
265. 確保測試通過
27
28### 驗收條件
29- [ ] GetBookInteractor 正確注入 IBookRepository
30- [ ] execute 方法實作正確
31- [ ] 異常處理完整
32- [ ] 單元測試 100% 通過(至少 4 個測試)
33- [ ] dart analyze 0 錯誤
34
35### 指標評估
36- 職責數量:2 個(業務協調、異常處理)
37- 程式碼行數:~55 行(含測試)
38- 涉及檔案:1 個
39- 測試用例:4 個
40→ Level 2(中等)
41
42### 預估時間
4330-40 分鐘

4.3 複雜 Ticket 標準與範例(Level 3)

Level 3 特徵

  • 職責:3-5 個相關職責
  • 行數:70-100 行
  • 檔案:3-5 個
  • 測試:6-10 個

適用場景

  • 完整 Repository CRUD
  • 複雜 UseCase(含多重驗證)
  • 複雜業務邏輯實作

注意:Level 3 Ticket 建議優先評估是否可拆分為更小 Ticket


範例 5:實作 BookRepository CRUD(建議拆分)

 1## Ticket #5: 實作 SQLiteBookRepository CRUD
 2
 3### 層級:Layer 5 (Infrastructure)
 4
 5### 業務需求
 6REQ-LIB-001: 書籍資料存取功能
 7
 8### 目標
 9實作 `SQLiteBookRepository`,提供完整的 CRUD 操作
10
11### 依賴 Ticket
12- Ticket #1: 定義 IBookRepository 介面(必須先完成)
13
14### 複雜度警告
15此 Ticket 為 Level 3(複雜),建議拆分為 3 個 Level 2 Ticket:
16
17- Ticket A: 實作 getBookByIsbn + 測試
18- Ticket B: 實作 saveBook + 測試
19- Ticket C: 實作 deleteBook + Data Mapper + 測試
20
21### 步驟(如不拆分)
221.`lib/infrastructure/repositories/` 建立 `sqlite_book_repository.dart`
232.`lib/infrastructure/mappers/` 建立 `book_mapper.dart`
243. 實作 `getBookByIsbn` 方法
25   - SQL 查詢邏輯
26   - Data Mapper 轉換(DTO → Entity)
27   - 錯誤處理
284. 實作 `saveBook` 方法
29   - SQL 插入/更新邏輯
30   - Data Mapper 轉換(Entity → DTO)
31   - 錯誤處理
325. 實作 `deleteBook` 方法
336. 撰寫完整測試
34   - getBookByIsbn: 成功、不存在、異常(3 個測試)
35   - saveBook: 新增、更新、異常(3 個測試)
36   - deleteBook: 成功、不存在、異常(3 個測試)
377. 確保所有測試通過
38
39### 驗收條件
40- [ ] 實作所有 IBookRepository 方法
41- [ ] Data Mapper 轉換正確
42- [ ] 異常處理完整
43- [ ] 單元測試 100% 通過(至少 9 個測試)
44- [ ] dart analyze 0 錯誤
45
46### 指標評估
47- 職責數量:5 個(get、save、delete、mapper、異常處理)
48- 程式碼行數:~160 行(含測試)
49- 涉及檔案:2 個
50- 測試用例:9 個
51→ Level 3(複雜)建議拆分
52
53### 預估時間
5460-90 分鐘
55
56### 建議拆分方案
57拆分為 3 個 Ticket(詳見第五章決策樹)

4.4 必須拆分標準(Level 4)

Level 4 特徵

  • 職責:> 5 個
  • 行數:> 100 行
  • 檔案:> 5 個
  • 測試:> 10 個

處理方式

  • 禁止建立 Level 4 Ticket
  • 必須拆分為多個 Level 1-2 Ticket
  • 拆分後可接受少數 Level 3 Ticket

範例 6:God Ticket 檢測與拆分

 1## 禁止:實作完整書籍評分功能
 2
 3### 原始任務描述
 4實作書籍評分功能,包含 UI、Controller、UseCase、Repository
 5
 6### God Ticket 檢測結果
 7- 職責數量:8 個 → Level 4
 8- 程式碼行數:~300 行 → Level 4
 9- 涉及檔案:8 個 → Level 4
10- 測試用例:20 個 → Level 4
11**所有指標都超標,必須拆分**
12
13### 拆分決策
14使用「Clean Architecture 分層拆分策略」(詳見第三章 3.4)
15
16拆分為 6 個 Ticket:
171. Ticket #201: Rating Value Object(Level 2)
182. Ticket #202: Rating Entity(Level 1)
193. Ticket #203: IRatingRepository Interface(Level 1)
204. Ticket #204: SQLiteRatingRepository(Level 3)
215. Ticket #205: RateBookUseCase(Level 2)
226. Ticket #206: RatingWidget UI(Level 2)
23
24結果:5 個 Level 1-2 + 1 個 Level 3 全部可接受

4.5 Ticket 大小對照表

快速參考表

Level職責行數檔案測試預估時間範例
1 簡單1<3011-35-20分鐘Interface 定義、簡單 VO
2 中等2-330-702-33-620-40分鐘Entity、基礎 UseCase
3 複雜3-570-1003-56-1040-90分鐘完整 Repository CRUD
4 超標>5>100>5>10N/A禁止建立

決策建議

  • Level 1-2:直接建立 Ticket
  • Level 3:優先評估是否可拆分
  • Level 4:必須拆分,無例外

第五章:拆分決策樹

5.1 決策樹總覽

完整決策流程

 1[Ticket 設計階段]
 2 3[計算 4 個量化指標]
 4 5[取最高複雜度等級]
 6 7    ├─ Level 1-2
 8    │   ↓
 9    │   [直接建立 Ticket]
10    │   ↓
11    │   [進入 Phase 2(測試設計)]
1213    ├─ Level 3
14    │   ↓
15    │   [拆分評估決策]
16    │   ↓
17    │   ├─ 可拆分?
18    │   │   ├─ Yes → [執行拆分策略] → [重新評估]
19    │   │   └─ No → [勉強接受] → [標記高風險]
20    │   ↓
21    │   [進入 Phase 2]
2223    └─ Level 4
2425        [阻止建立 Ticket]
2627        [執行強制拆分]
2829        [選擇拆分策略]
3031        ├─ 優先:按架構分層拆分
32        ├─ 次之:按職責拆分
33        └─ 最後:按檔案拆分
3435        [重新評估所有子 Ticket]
3637        [確保所有子 Ticket ≤ Level 3]
3839        [可建立 Ticket]

5.2 Level 3 拆分評估決策

決策問題:此 Level 3 Ticket 是否應該拆分?

評估準則

準則 1:職責可分離性

 1問題:職責是否可獨立分離為多個 Ticket?
 2
 3評估方法:
 41. 列出所有職責
 52. 檢查職責之間的依賴關係
 63. 判斷是否可獨立完成
 7
 8範例:
 9原始任務:實作 BookRepository CRUD
10職責分析:
11
12- 職責 1:getBookByIsbn → 可獨立
13- 職責 2:saveBook → 可獨立(依賴職責 1 的測試模式)
14- 職責 3:deleteBook → 可獨立
15- 職責 4:Data Mapper → 可整合到職責 1-3
16- 職責 5:異常處理 → 可整合到職責 1-3
17
18結論:可拆分為 3 個 Ticket(每個職責對應 1 個 Ticket)

準則 2:拆分後複雜度降低

 1問題:拆分後每個子 Ticket 是否 ≤ Level 2?
 2
 3評估方法:
 4對每個拆分後的子 Ticket 重新計算 4 個指標
 5
 6範例:
 7原始 Ticket(Level 3):
 8
 9- 職責:5 個
10- 行數:160 行
11- 檔案:2 個
12- 測試:9 個
13
14拆分後 Ticket A:getBookByIsbn
15- 職責:2 個(查詢 + 轉換)
16- 行數:50 行
17- 檔案:2 個
18- 測試:3 個
19→ Level 2
20
21拆分後 Ticket B:saveBook
22- 職責:2 個(儲存 + 異常處理)
23- 行數:60 行
24- 檔案:1 個(修改)
25- 測試:3 個
26→ Level 2
27
28拆分後 Ticket C:deleteBook
29- 職責:2 個(刪除 + 異常處理)
30- 行數:50 行
31- 檔案:1 個(修改)
32- 測試:3 個
33→ Level 2
34
35結論:拆分成功,所有子 Ticket 都降為 Level 2

準則 3:拆分成本合理性

 1問題:拆分帶來的管理成本是否合理?
 2
 3成本考量:
 4
 5- 收益:風險降低、易於 Review、可並行開發
 6- 成本:增加 Ticket 數量、需要管理依賴關係
 7
 8決策:
 9
10- 拆分收益 > 拆分成本 → 建議拆分
11- 拆分收益 ≈ 拆分成本 → 可選擇
12- 拆分收益 < 拆分成本 → 不建議拆分
13
14範例:
15原始 Ticket:Level 3(複雜 Repository CRUD)
16拆分收益:
17+ 風險降低:1 個高風險 → 3 個低風險
18+ Review 效率:每次 Review 50-60 行 vs 160 行
19+ 可並行:3 個 Ticket 可依序快速開發
20
21拆分成本:
22
23- 管理成本:需要管理 3 個 Ticket 依賴
24- 整合測試:需要額外的整合測試(但本來就需要)
25
26結論:拆分收益 > 拆分成本,建議拆分

5.3 Level 4 強制拆分策略

Level 4 無需評估,必須拆分

拆分策略優先順序

策略 1:按 Clean Architecture 分層拆分(優先)
 1適用情況:
 2
 3- Ticket 跨越多個架構層級(Layer 1-5)
 4- 檔案分布在不同層級目錄
 5- 涉及 UI、UseCase、Repository 等多層
 6
 7拆分方法:
 81. 按照 Layer 1 → Layer 5 順序分組檔案
 92. 每個 Layer 建立獨立 Ticket
103. 相鄰兩層可合併為單一 Ticket(如 Interface + Implementation)
11
12範例(書籍評分功能):
13原始:8 個檔案,跨越 4 層
14拆分後:
15
16- Ticket 1: Layer 5 Domain(Rating VO + Entity)
17- Ticket 2: Layer 5 + 4 Repository(Interface + Impl)
18- Ticket 3: Layer 3 UseCase
19- Ticket 4: Layer 1-2 Presentation(UI + Controller)
20
21結果:4 個 Level 1-2 Ticket
策略 2:按職責拆分(次之)
 1適用情況:
 2
 3- 所有檔案在同一層級,但職責過多
 4- 單一 Repository 包含過多 CRUD 方法
 5- 單一 UseCase 包含過多業務邏輯
 6
 7拆分方法:
 81. 列出所有職責
 92. 每個獨立職責建立 Ticket
103. 相關職責可合併(最多 2-3 個職責)
11
12範例(BookRepository CRUD):
13原始:5 個職責(get + save + delete + mapper + error)
14拆分後:
15
16- Ticket A: getBookByIsbn + mapper + error(2-3 職責)
17- Ticket B: saveBook + error(2 職責)
18- Ticket C: deleteBook + error(2 職責)
19
20結果:3 個 Level 2 Ticket
策略 3:按檔案拆分(最後)
 1適用情況:
 2
 3- 前兩種策略都無法適用
 4- 檔案之間相對獨立
 5- 每個檔案本身就是一個完整單元
 6
 7拆分方法:
 81. 每個檔案建立獨立 Ticket
 92. 相關檔案可合併(最多 2-3 個檔案)
10
11範例:
12原始:7 個檔案
13拆分後:
14
15- Ticket 1: file1.dart + file2.dart(相關)
16- Ticket 2: file3.dart
17- Ticket 3: file4.dart + file5.dart(相關)
18- Ticket 4: file6.dart + file7.dart(相關)
19
20結果:4 個 Ticket

5.4 拆分決策檢查清單

Level 3 拆分評估檢查清單

1□ 已列出所有職責
2□ 已評估職責可分離性
3□ 已計算拆分後每個子 Ticket 的指標
4□ 已確認所有子 Ticket ≤ Level 2
5□ 已評估拆分成本 vs 收益
6□ 已確定是否拆分的最終決策

Level 4 強制拆分檢查清單

1□ 已識別 Ticket 為 Level 4(任一指標超標)
2□ 已選擇拆分策略(分層 / 職責 / 檔案)
3□ 已執行拆分(列出所有子 Ticket)
4□ 已重新評估所有子 Ticket
5□ 已確認所有子 Ticket ≤ Level 3
6□ 已標記子 Ticket 依賴關係
7□ 已確認子 Ticket 總和涵蓋原始功能

第六章:Ticket 拆分檢查清單

6.1 拆分前檢查清單

階段 1:需求理解

1□ 已閱讀完整的業務需求
2□ 已理解 Ticket 的業務目標
3□ 已確認 Ticket 的驗收條件
4□ 已識別所有需要完成的功能點
5□ 已確認與其他 Ticket 的依賴關係
階段 2:指標計算
1□ 已列出所有職責(功能點 + 邊界條件 + 異常處理)
2□ 已估算程式碼行數(參考類似任務)
3□ 已列出所有涉及檔案(含新建和修改)
4□ 已估算測試用例數(正常 + 邊界 + 異常)
5□ 已取最高複雜度等級作為最終評估
階段 3:複雜度確認
1□ 已確定 Ticket 的複雜度等級(Level 1-4)
2□ 如為 Level 4,已阻止建立並準備拆分
3□ 如為 Level 3,已評估是否拆分
4□ 如為 Level 1-2,已確認可直接建立

6.2 拆分過程檢查清單

階段 4:拆分策略選擇

1□ 已分析 Ticket 涉及的架構層級
2□ 已確定拆分策略(分層 / 職責 / 檔案)
3□ 已列出所有拆分後的子 Ticket
4□ 已為每個子 Ticket 撰寫初步描述
階段 5:子 Ticket 設計
1□ 每個子 Ticket 都有明確的目標
2□ 每個子 Ticket 都有清楚的步驟
3□ 每個子 Ticket 都有具體的驗收條件
4□ 每個子 Ticket 都標記了層級([Layer X])
5□ 每個子 Ticket 都標記了依賴關係
階段 6:依賴關係確認
1□ 已識別所有子 Ticket 之間的依賴
2□ 已確保依賴方向符合 Clean Architecture 規則
3□ 已標記依賴順序(Phase 1 → Phase 2 → ...)
4□ 已確認可並行執行的 Ticket

6.3 拆分後驗證清單

階段 7:指標重新評估

1□ 已重新計算每個子 Ticket 的 4 個指標
2□ 已確認所有子 Ticket ≤ Level 3
3□ 已確認大多數子 Ticket ≤ Level 2
4□ 如有 Level 3 子 Ticket,已評估合理性
階段 8:完整性驗證
1□ 所有子 Ticket 功能總和 = 原始 Ticket 功能
2□ 沒有遺漏任何功能點
3□ 沒有重複的功能實作
4□ 所有檔案都被包含在某個子 Ticket 中
5□ 所有測試案例都被包含
階段 9:品質檢查
1□ 每個子 Ticket 都有業務需求引用
2□ 每個子 Ticket 都有指標評估
3□ 每個子 Ticket 都有預估時間
4□ 每個子 Ticket 的職責明確且不重疊
5□ 每個子 Ticket 的標題包含 [Layer X] 標籤

6.4 特殊情況檢查清單

情況 1:無法拆分的 Level 3 Ticket

1□ 已明確記錄為何無法拆分
2□ 已標記為「高風險 Ticket」
3□ 已安排額外的 Code Review
4□ 已增加測試覆蓋率要求(> 90%)
5□ 已準備更長的開發時間
情況 2:跨層整合 Ticket
1□ 已確認只涉及相鄰兩層(如 Layer 3 + Layer 4-5)
2□ 已確認符合依賴規則(外層 → 內層)
3□ 已明確標記為「整合 Ticket」
4□ 已包含整合測試驗收條件
情況 3:測試獨立 Ticket
1□ 已確認生產程式碼已完成(依賴 Ticket)
2□ Ticket 只包含測試程式碼
3□ 已列出所有測試案例(正常 + 邊界 + 異常)
4□ 已確認測試數量在合理範圍(< 10 

第七章:實務案例與最佳實踐

7.1 完整案例:書籍搜尋功能

業務需求

1REQ-SEARCH-001: 實作書籍搜尋功能
2- 使用者可輸入關鍵字搜尋書籍
3- 支援書名、作者、ISBN 搜尋
4- 顯示搜尋結果列表

初步評估:識別為 God Ticket

 1原始任務分析:
 2實作完整書籍搜尋功能(包含 UI、Controller、UseCase、Repository)
 3
 4指標計算:
 5
 6- 職責數量:10 個
 7  1. SearchBar UI 元件
 8  2. SearchResultList UI 元件
 9  3. SearchController 狀態管理
10  4. SearchBookUseCase 業務協調
11  5. Repository 查詢方法(書名搜尋)
12  6. Repository 查詢方法(作者搜尋)
13  7. Repository 查詢方法(ISBN 搜尋)
14  8. 錯誤處理
15  9. 載入狀態處理
16  10. 空結果處理
17
18- 程式碼行數:~400 行
19- 涉及檔案:10 個
20- 測試用例:25 個
21
22複雜度評估:
23
24- 職責:10 個 → Level 4
25- 行數:400 行 → Level 4
26- 檔案:10 個 → Level 4
27- 測試:25 個 → Level 4
28
29結論:God Ticket 必須拆分

拆分策略:Clean Architecture 分層拆分

第一步:按層級分組檔案
 1Layer 5 (Domain + Infrastructure):
 2- SearchQuery Value Object
 3- IBookRepository.searchByTitle()
 4- IBookRepository.searchByAuthor()
 5- IBookRepository.searchByIsbn()
 6- SQLiteBookRepository 查詢實作
 7
 8Layer 3 (UseCase):
 9- SearchBookUseCase
10
11Layer 2 (Behavior):
12- SearchController
13
14Layer 1 (UI):
15- SearchBar Widget
16- SearchResultList Widget
第二步:設計拆分後的 Ticket

Ticket 1: 定義 SearchQuery Value Object(Layer 5)
 1## Ticket #301: 定義 SearchQuery Value Object
 2
 3### 層級:Layer 5 (Domain Implementation)
 4### 策略:策略 2(具體實作)
 5
 6### 業務需求
 7REQ-SEARCH-001: 書籍搜尋功能
 8
 9### 目標
10建立 `SearchQuery` Value Object,封裝搜尋條件驗證
11
12### 步驟
131. 建立 `lib/domain/value_objects/search_query.dart`
142. 實作 SearchQuery 類別
15   - 驗證查詢字串長度(最少 2 個字元)
16   - trim() 處理空白
17   - equals / hashCode
183. 撰寫單元測試
194. 確保測試通過
20
21### 驗收條件
22- [ ] SearchQuery 類別實作完整
23- [ ] 驗證邏輯正確(最少 2 字元)
24- [ ] 單元測試 100% 通過(至少 4 個測試)
25- [ ] dart analyze 0 錯誤
26
27### 指標評估
28- 職責:1 個
29- 行數:~30 行
30- 檔案:1 個
31- 測試:4 個
32→ Level 1(簡單)
33
34### 預估時間
3515-20 分鐘

Ticket 2: 擴充 IBookRepository 查詢方法(Layer 4)

 1## Ticket #302: 定義 IBookRepository 搜尋方法
 2
 3### 層級:Layer 4 (Domain Interfaces)
 4### 策略:策略 1(Interface 定義)
 5
 6### 業務需求
 7REQ-SEARCH-001: 書籍搜尋功能
 8
 9### 目標
10`IBookRepository` 介面新增搜尋方法簽名
11
12### 步驟
131. 修改 `lib/domain/repositories/i_book_repository.dart`
142. 新增 `searchByTitle(String query)` 方法簽名
153. 新增 `searchByAuthor(String query)` 方法簽名
164. 新增 `searchByIsbn(String query)` 方法簽名
175. 撰寫文檔註解
18
19### 驗收條件
20- [ ] 3 個搜尋方法簽名完整
21- [ ] 回傳類型為 `Future<List<Book>>`
22- [ ] 文檔註解包含需求編號
23- [ ] dart analyze 0 錯誤
24
25### 指標評估
26- 職責:1 個(定義介面)
27- 行數:~15 行
28- 檔案:1 個(修改)
29- 測試:0 個
30→ Level 1(簡單)
31
32### 預估時間
3310 分鐘

Ticket 3: 實作 BookRepository 搜尋方法(Layer 5)

 1## Ticket #303: 實作 SQLiteBookRepository 搜尋
 2
 3### 層級:Layer 5 (Infrastructure)
 4### 策略:策略 2(具體實作)
 5### 依賴:Ticket #301, #302
 6
 7### 業務需求
 8REQ-SEARCH-001: 書籍搜尋功能
 9
10### 目標
11實作 `SQLiteBookRepository` 的 3 個搜尋方法
12
13### 步驟
141. 修改 `lib/infrastructure/repositories/sqlite_book_repository.dart`
152. 實作 `searchByTitle` 方法
16   - SQL LIKE 查詢
17   - Data Mapper 轉換
18   - 錯誤處理
193. 實作 `searchByAuthor` 方法
204. 實作 `searchByIsbn` 方法
215. 撰寫單元測試(每個方法 2-3 個測試)
226. 確保測試通過
23
24### 驗收條件
25- [ ] 3 個搜尋方法實作完整
26- [ ] SQL 查詢使用 LIKE 模糊搜尋
27- [ ] 異常處理完整
28- [ ] 單元測試 100% 通過(至少 8 個測試)
29- [ ] dart analyze 0 錯誤
30
31### 指標評估
32- 職責:4 個(3 個搜尋方法 + 異常處理)
33- 行數:~95 行
34- 檔案:1 個(修改)
35- 測試:8 個
36→ Level 3(複雜)可接受
37(註:3 個搜尋方法邏輯相似,整合實作較有效率)
38
39### 預估時間
4060-75 分鐘

Ticket 4: 實作 SearchBookUseCase(Layer 3)

 1## Ticket #304: 實作 SearchBookUseCase
 2
 3### 層級:Layer 3 (UseCase)
 4### 策略:策略 4(整合連接)
 5### 依賴:Ticket #302, #303
 6
 7### 業務需求
 8REQ-SEARCH-001: 書籍搜尋功能
 9
10### 目標
11實作 `SearchBookUseCase`,協調搜尋業務流程
12
13### 步驟
141. 建立 `lib/application/use_cases/search_book_use_case.dart`
152. 注入 `IBookRepository`
163. 實作 `execute(SearchQuery query, SearchType type)` 方法
17   - 根據 SearchType 呼叫對應的 Repository 方法
18   - 處理空結果
19   - 處理異常
204. 撰寫整合測試
215. 確保測試通過
22
23### 驗收條件
24- [ ] UseCase 正確注入 IBookRepository
25- [ ] 支援 3 種搜尋類型(Title / Author / ISBN)
26- [ ] 整合測試 100% 通過(至少 5 個測試)
27- [ ] 異常處理完整
28- [ ] dart analyze 0 錯誤
29
30### 指標評估
31- 職責:2 個(業務協調、異常處理)
32- 行數:~65 行
33- 檔案:1 個
34- 測試:5 個
35→ Level 2(中等)
36
37### 預估時間
3835-45 分鐘

Ticket 5: 實作 SearchController(Layer 2)

 1## Ticket #305: 實作 SearchController
 2
 3### 層級:Layer 2 (Behavior)
 4### 策略:策略 4(整合連接)
 5### 依賴:Ticket #304
 6
 7### 業務需求
 8REQ-SEARCH-001: 書籍搜尋功能
 9
10### 目標
11實作 `SearchController`,管理搜尋狀態
12
13### 步驟
141. 建立 `lib/presentation/controllers/search_controller.dart`
152. 注入 `SearchBookUseCase`
163. 實作狀態管理
17   - isLoading(載入中)
18   - searchResults(搜尋結果)
19   - errorMessage(錯誤訊息)
204. 實作 `search(String query, SearchType type)` 方法
215. 撰寫單元測試
226. 確保測試通過
23
24### 驗收條件
25- [ ] SearchController 正確注入 UseCase
26- [ ] 狀態管理正確(loading / success / error)
27- [ ] 單元測試 100% 通過(至少 5 個測試)
28- [ ] dart analyze 0 錯誤
29
30### 指標評估
31- 職責:2 個(狀態管理、UseCase 整合)
32- 行數:~70 行
33- 檔案:1 個
34- 測試:5 個
35→ Level 2(中等)
36
37### 預估時間
3840 分鐘

Ticket 6: 實作搜尋 UI 元件(Layer 1)

 1## Ticket #306: 實作 SearchBar 和 SearchResultList
 2
 3### 層級:Layer 1 (UI)
 4### 策略:策略 2(具體實作)
 5### 依賴:Ticket #305
 6
 7### 業務需求
 8REQ-SEARCH-001: 書籍搜尋功能
 9
10### 目標
11實作搜尋介面的 UI 元件
12
13### 步驟
141. 建立 `lib/presentation/widgets/search_bar.dart`
152. 實作 SearchBar Widget
16   - TextField 輸入框
17   - SearchType 選擇器
18   - 搜尋按鈕
193. 建立 `lib/presentation/widgets/search_result_list.dart`
204. 實作 SearchResultList Widget
21   - 顯示搜尋結果
22   - 載入中狀態
23   - 空結果提示
24   - 錯誤提示
255. 撰寫 Widget 測試
266. 確保測試通過
27
28### 驗收條件
29- [ ] SearchBar UI 正確顯示和互動
30- [ ] SearchResultList 正確顯示各種狀態
31- [ ] Widget 測試 100% 通過(至少 6 個測試)
32- [ ] dart analyze 0 錯誤
33
34### 指標評估
35- 職責:2 個(SearchBar、SearchResultList)
36- 行數:~90 行
37- 檔案:2 個
38- 測試:6 個
39→ Level 2(中等)
40
41### 預估時間
4250-60 分鐘

拆分結果總結

拆分前後對比

項目拆分前(God Ticket)拆分後(6 個 Ticket)
職責數量10 個平均 1.8 個
程式碼行數~400 行平均 61 行
檔案數量10 個平均 1.2 個
測試數量25 個平均 4.7 個
複雜度等級Level 45 個 Level 1-2
1 個 Level 3
預估總時間N/A(無法估算)210-250 分鐘

拆分效益分析

  1. 風險大幅降低

    • 從 1 個無法管理的 God Ticket → 6 個可控 Ticket
    • 單一 Ticket 失敗不影響其他 Ticket
  2. 支援並行開發

    • Phase 1: Ticket #301, #302 可並行
    • Phase 2: Ticket #303 執行
    • Phase 3: Ticket #304 執行
    • Phase 4: Ticket #305, #306 可部分並行
  3. Review 效率提升

    • 每次 Review 平均 61 行(vs 400 行)
    • Review 時間縮短 85%
  4. 測試獨立性

    • 每層可獨立測試
    • Mock 依賴簡單明確

7.2 常見錯誤與解決方案

錯誤 1:過度拆分

問題描述

 1將 Level 2(中等)Ticket 拆分為多個 Level 1 Ticket,
 2反而增加管理成本且沒有實質收益。
 3
 4範例:
 5原始 Ticket(Level 2):實作 Rating Value Object
 6- 職責:2 個(建立 + 驗證)
 7- 行數:50 行
 8- 測試:5 個
 9
10過度拆分為 2 個 Ticket:
11
12- Ticket A:實作 Rating 建構子(Level 1,25 行)
13- Ticket B:實作 Rating 驗證邏輯(Level 1,25 行)
14
15問題:
16
17- 兩個 Ticket 高度耦合,必須連續執行
18- 增加了額外的管理成本
19- 沒有實質的風險降低

解決方案

1保持原始 Level 2 Ticket 不拆分
2
3判斷準則:
4
5- Level 1-2 Ticket 通常不需要拆分
6- 只有 Level 3-4 才需要評估拆分
7- 拆分後的子 Ticket 應該相對獨立

錯誤 2:拆分後依賴過於複雜

問題描述

 1拆分方式導致依賴關係過於複雜,
 2難以理解和管理執行順序。
 3
 4範例:
 5拆分為 8 個 Ticket,依賴關係如下:
 6Ticket 1 → Ticket 2 → Ticket 4
 7Ticket 1 → Ticket 3 → Ticket 5
 8Ticket 2 → Ticket 6
 9Ticket 3 → Ticket 6
10Ticket 5 → Ticket 7
11Ticket 6 → Ticket 7 → Ticket 8
12
13問題:
14
15- 依賴網絡複雜,難以追蹤
16- 執行順序不明確
17- 容易遺漏依賴

解決方案

 1使用線性或樹狀依賴結構
 2
 3優化後的依賴關係:
 4Phase 1(並行):
 5├─ Ticket 1, Ticket 2
 6
 7Phase 2(並行):
 8├─ Ticket 3, Ticket 4(依賴 Phase 1)
 9
10Phase 3:
11└─ Ticket 5(依賴 Phase 2)
12
13Phase 4:
14└─ Ticket 6(依賴 Phase 3)
15
16判斷準則:
17
18- 優先使用 Phase 分組(線性依賴)
19- 同 Phase 內的 Ticket 可並行
20- 避免跨 Phase 的複雜依賴

錯誤 3:拆分邊界不清晰

問題描述

 1拆分後的子 Ticket 職責重疊或邊界模糊,
 2導致實作時不知道該在哪個 Ticket 完成。
 3
 4範例:
 5原始:實作完整 BookRepository
 6拆分後:
 7
 8- Ticket A:實作 getBookByIsbn
 9- Ticket B:實作 Data Mapper
10- Ticket C:實作異常處理
11
12問題:
13
14- Ticket A 需要 Data Mapper(依賴 Ticket B)
15- Ticket A 需要異常處理(依賴 Ticket C)
16- 但 Ticket A 應該先完成?
17- 邊界模糊,無法獨立完成

解決方案

 1確保每個子 Ticket 可獨立完成
 2
 3正確拆分:
 4
 5- Ticket A:實作 getBookByIsbn(含 Mapper + 異常處理)
 6- Ticket B:實作 saveBook(含 Mapper + 異常處理)
 7- Ticket C:實作 deleteBook(含異常處理)
 8
 9判斷準則:
10
11- 每個子 Ticket 應該是「最小可交付單元」
12- 避免將共用邏輯獨立為 Ticket
13- 共用邏輯應整合到使用它的 Ticket 中

7.3 拆分最佳實踐

實踐 1:優先選擇架構分層拆分

原則

1當 Ticket 跨越多個架構層級時,
2優先按照 Clean Architecture 分層拆分。
3
4優點:
5依賴方向清晰(內層不依賴外層)
6測試策略明確(每層有標準測試方法)
7可並行開發(不同層可由不同開發者負責)
8符合單一職責原則

範例

 1原始任務:實作書籍評分功能(跨越 4 層)
 2
 3錯誤:按功能模組拆分
 4- Ticket A:評分輸入功能(UI + Controller + UseCase)
 5- Ticket B:評分儲存功能(Repository + Mapper)
 6
 7問題:
 8
 9- Ticket A 跨越 3 層,依賴關係複雜
10- 測試困難(需要 Mock 多層)
11
12正確:按架構分層拆分
13- Ticket 1:Rating Domain 模型(Layer 5)
14- Ticket 2:IRatingRepository + Impl(Layer 4-5)
15- Ticket 3:RateBookUseCase(Layer 3)
16- Ticket 4:RatingController + Widget(Layer 1-2)
17
18優點:
19
20- 每個 Ticket 職責單一
21- 依賴方向清楚
22- 測試獨立簡單

實踐 2:保持線性依賴順序

原則

 1優先設計線性或樹狀依賴結構,
 2避免複雜的網狀依賴關係。
 3
 4建議結構:
 5線性依賴:Ticket 1 → Ticket 2 → Ticket 3 → Ticket 4
 6樹狀依賴:
 7   Ticket 1
 8   ├─ Ticket 2
 9   │  └─ Ticket 4
10   └─ Ticket 3
11      └─ Ticket 5
12
13避免結構:
14網狀依賴:多個交叉依賴,難以追蹤

實施方法

 1步驟 1:識別「核心 Ticket」
 2- Domain 層 Ticket 通常是核心
 3- 其他層依賴 Domain 層
 4
 5步驟 2:按照 Clean Architecture 依賴方向排序
 6- Layer 5 → Layer 4 → Layer 3 → Layer 2 → Layer 1
 7
 8步驟 3:標記 Phase
 9- Phase 1:Layer 5(Domain)
10- Phase 2:Layer 4-5(Repository)
11- Phase 3:Layer 3(UseCase)
12- Phase 4:Layer 1-2(Presentation)

實踐 3:每個 Ticket 包含完整測試

原則

1每個 Ticket 應該包含對應的測試,
2不要將測試獨立為單獨 Ticket(除非測試數量 > 10)。
3
4優點:
5TDD 紅綠燈循環完整
6每個 Ticket 交付時就是可測試的
7避免「實作完成但測試缺失」的情況

實施方法

 1在 Ticket 驗收條件中明確測試要求:
 2
 3範例:
 4## Ticket #X: 實作 Rating Value Object
 5
 6### 驗收條件
 7- [ ] Rating 類別實作完整
 8- [ ] 單元測試 100% 通過(至少 3 個測試)
 9  - 測試 1:建立有效評分
10  - 測試 2:評分過低拋出異常
11  - 測試 3:評分過高拋出異常
12- [ ] 測試覆蓋率 > 90%
13- [ ] dart analyze 0 錯誤
14
15例外情況(測試獨立 Ticket):
16
17- 測試用例 > 10 個(測試複雜度 Level 4)
18- 補充現有程式碼的測試(非新功能)
19- 整合測試(跨多層驗證)

實踐 4:明確標記層級和策略

原則

 1每個 Ticket 標題應包含:
 21. [Layer X] 標籤(標示架構層級)
 32. 策略類型(Interface / 實作 / 整合)
 4
 5格式:
 6## Ticket #NNN: [Layer X] {動詞} {目標}
 7
 8範例:
 9好的標題:
10
11- [Layer 4] 定義 IBookRepository 介面
12- [Layer 5] 實作 SQLiteBookRepository
13- [Layer 3] 實作 GetBookUseCase
14
15不好的標題:
16
17- 書籍資料存取功能(沒有層級、目標不明確)
18- 實作 Repository(沒有層級、範圍不清楚)

實施方法

 1在 Ticket 範本中包含:
 2
 3### 層級:Layer X (層級名稱)
 4### 策略:策略 N(策略名稱)
 5
 6範例:
 7## Ticket #101: [Layer 4] 定義 IBookRepository 介面
 8
 9### 層級:Layer 4 (Domain Interfaces)
10### 策略:策略 1(Interface 定義)
11
12### 目標
13建立 `IBookRepository` 介面,定義書籍資料存取的契約
14
15這樣可以:
16快速識別 Ticket 的架構位置
17理解 Ticket 的目的(定義 / 實作 / 整合)
18追蹤依賴關係(內層 → 外層)

附錄 A:術語表

術語英文定義範例
職責ResponsibilityTicket 需要完成的獨立功能點或邊界條件定義介面、實作方法、處理異常
複雜度等級Complexity Level基於 4 個量化指標計算的任務複雜度(Level 1-4)Level 2(中等)
God TicketGod Ticket範圍過大、無法管理的任務(Level 4)包含 8 個檔案、300 行程式碼的任務
單層修改原則Single Layer Modification每個 Ticket 應只修改單一架構層級Ticket 只修改 Domain 層
依賴規則Dependency RuleClean Architecture 的依賴方向規則(外層→內層)UseCase 依賴 Repository Interface
最小可交付單元Minimum Deliverable Unit可獨立完成、測試、交付的最小功能單元實作單一 Value Object
指標整合評估Integrated Indicator Assessment取 4 個指標中最高的複雜度等級max(Level 2, Level 3, Level 1, Level 2) = Level 3
拆分策略Splitting Strategy將大任務拆分為小任務的標準方法按架構分層拆分
PhasePhase依賴順序的執行階段Phase 1(Domain 層)→ Phase 2(Repository 層)

附錄 B:拆分決策模板

模板用途:用於記錄 Ticket 拆分的決策過程

 1# Ticket 拆分決策記錄
 2
 3## 基本資訊
 4- **原始任務**:[任務描述]
 5- **業務需求**:[需求編號]
 6- **評估日期**:YYYY-MM-DD
 7- **評估人員**:[姓名]
 8
 9## 指標評估
10
11### 職責數量
12- **職責列表**:
13  1. [職責 1]
14  2. [職責 2]
15  ...
16- **總計**:X 個
17- **等級**:Level X
18
19### 程式碼行數
20- **預估行數**:X 行
21- **依據**:[參考類似任務或經驗估算]
22- **等級**:Level X
23
24### 涉及檔案
25- **檔案列表**:
26  1. [檔案路徑 1]
27  2. [檔案路徑 2]
28  ...
29- **總計**:X 個
30- **等級**:Level X
31
32### 測試用例數
33- **測試列表**:
34  1. [測試案例 1]
35  2. [測試案例 2]
36  ...
37- **總計**:X 個
38- **等級**:Level X
39
40## 複雜度結論
41- **最高等級**:Level X
42- **最終判定**:[簡單 / 中等 / 複雜 / 必須拆分]
43
44## 拆分決策
45
46### 決策結果
47- [ ] 不拆分(Level 1-2)
48- [ ] 評估拆分(Level 3)
49- [ ] 強制拆分(Level 4)
50
51### 拆分原因(如適用)
52[說明為什麼需要拆分]
53
54### 拆分策略(如適用)
55- [ ] 按架構分層拆分(優先)
56- [ ] 按職責拆分(次之)
57- [ ] 按檔案拆分(最後)
58
59## 拆分方案(如適用)
60
61### 子 Ticket 列表
621. **Ticket A**:[標題]
63   - 層級:Layer X
64   - 指標:職責 X、行數 X、檔案 X、測試 X
65   - 等級:Level X
66
672. **Ticket B**:[標題]
68   - 層級:Layer X
69   - 指標:職責 X、行數 X、檔案 X、測試 X
70   - 等級:Level X
71
72### 依賴關係
73```text
74[依賴關係圖或順序說明]

執行順序

  • Phase 1:[Ticket A, Ticket B]
  • Phase 2:[Ticket C]

驗證清單

拆分前檢查

  • 已計算所有 4 個指標
  • 已確定複雜度等級
  • 已確定是否拆分

拆分後檢查(如適用)

  • 所有子 Ticket 已設計
  • 所有子 Ticket ≤ Level 3
  • 依賴關係明確
  • 功能完整性確認

備註

[其他需要記錄的資訊]

 1
 2---
 3
 4## 附錄 C:與其他方法論的整合指引
 5
 6### C.1 與 TDD 四階段方法論整合
 7
 8**整合點**:
 9
10```markdown
11本方法論位於 TDD Phase 1(設計階段)
12
13TDD 流程整合:
14Phase 1: Ticket 設計
1516  使用本方法論評估 Ticket 複雜度
1718  如為 Level 3-4,執行拆分
1920  所有 Ticket 確認為 Level 1-2
2122Phase 2: 測試設計(sage-test-architect)
2324Phase 3: 實作(pepper → parsley)
2526Phase 4: 重構(cinnamon-refactor-owl)

協作方式

  • Phase 1 完成時,所有 Ticket 都應該 ≤ Level 3
  • 理想狀態:所有 Ticket 都是 Level 1-2
  • 如有 Level 3 Ticket,應標記為「需要額外 Review」

C.2 與層級隔離派工方法論整合

整合點

1本方法論提供「拆分標準」
2層級隔離派工方法論提供「派工策略」
3
4協作流程:
51. 使用本方法論拆分 Ticket(按架構分層)
62. 使用層級隔離派工方法論決定執行順序
7   - 優先派發內層 Ticket(Layer 5)
8   - 逐步派發外層 Ticket(Layer 1)
93. 確保依賴順序符合 Clean Architecture 規則

範例

 1拆分結果(本方法論):
 2
 3- Ticket 1: Layer 5 Domain
 4- Ticket 2: Layer 4-5 Repository
 5- Ticket 3: Layer 3 UseCase
 6- Ticket 4: Layer 1-2 Presentation
 7
 8派工順序(層級隔離派工方法論):
 9Week 1:
10  ├─ 派發 Ticket 1(Layer 5)給 Developer A
11  ├─ Ticket 1 完成後,派發 Ticket 2(Layer 4-5)給 Developer A
12
13Week 2:
14  ├─ Ticket 2 完成後,派發 Ticket 3(Layer 3)給 Developer B
15  └─ 可同時派發 Ticket 4(Layer 1-2)給 Developer C(部分並行)

C.3 與 Code Smell 品質閘門檢測方法論整合

整合點

 1本方法論在 Ticket 設計階段執行(Design Time)
 2Code Smell 品質閘門在 Ticket 執行後檢測(Runtime)
 3
 4協作流程:
 5設計階段(本方法論):
 6  ├─ 計算 4 個指標
 7  ├─ 評估複雜度等級
 8  ├─ 決定是否拆分
 9  └─ 確保 Ticket ≤ Level 3
10
11執行階段(Code Smell 檢測):
12  ├─ C1 檢測:God Ticket(檔案數、層級跨度)
13  ├─ C2 檢測:Incomplete Ticket(缺失元素)
14  └─ C3 檢測:Ambiguous Responsibility(職責不明)
15
16如果 Code Smell 檢測失敗:
17  ├─ C1 失敗 → 返回本方法論重新拆分
18  ├─ C2 失敗 → 補充缺失元素
19  └─ C3 失敗 → 明確職責定義

防範措施

1如果在設計階段使用本方法論:
2C1 God Ticket 失敗率應該降至 0%
3C3 Ambiguous Responsibility 失敗率應該降至接近 0%
4C2 Incomplete Ticket 仍需要 Code Smell 檢測補強

C.4 與 Clean Architecture 實作方法論整合

整合點

1本方法論的「架構分層拆分策略」(第三章)
2完全基於 Clean Architecture 實作方法論的五層架構。
3
4依賴關係:
5本方法論 → 依賴 Clean Architecture 實作方法論
6- 採用相同的五層架構定義
7- 遵循相同的依賴規則
8- 使用相同的檔案路徑規範

參考章節對應

1本方法論第三章「Clean Architecture 分層拆分策略」
2↓ 引用自
3Clean Architecture 實作方法論:
4
5- 第一章:五層架構定義
6- 第二章:依賴規則
7- 第三章:檔案組織規範

總結

方法論核心價值

本方法論提供了量化、客觀、可執行的 Ticket 拆分標準,解決了傳統拆分方法的主觀性和不一致性問題。

快速開始指引

第一步:評估 Ticket 複雜度

11. 計算 4 個指標(職責、行數、檔案、測試)
22. 取最高複雜度等級
33. 判斷是否需要拆分

第二步:執行拆分(如需要)

11. 選擇拆分策略(優先:架構分層)
22. 設計子 Ticket
33. 重新評估每個子 Ticket
44. 確認所有子 Ticket ≤ Level 3

第三步:驗證和記錄

11. 使用檢查清單驗證(第六章)
22. 記錄決策過程(附錄 B 模板)
33. 進入 TDD Phase 2(測試設計)

相關方法論引用

完整的 Ticket 設計派工流程請參考: