專案規劃與需求分析
為什麼需要規劃?
💡 比喻:蓋房子先畫藍圖 你不會叫工人直接搬磚頭開始蓋房子吧? 一定是先找建築師畫好藍圖,確認幾間房間、幾間廁所、電線怎麼走。 寫程式也一樣——先規劃好,才不會蓋到一半才發現要拆牆重來。
沒有規劃的專案會怎樣?
沒有規劃的專案:
第 1 週:興致勃勃開始寫 code ✨
第 2 週:發現資料庫設計不對,砍掉重來 💣
第 3 週:客戶說「我要的不是這個」 😱
第 4 週:放棄 💀
有規劃的專案:
第 1 週:寫需求文件 + 畫流程圖 📐
第 2 週:確認規格 + 設計 Schema 📋
第 3 週:開始寫 code(方向清楚) 💻
第 4 週:第一版完成 + Demo 🎉
從想法到規格書
第一步:把想法寫下來
// 定義專案想法的類別 // Define project idea class
public class ProjectIdea // 專案想法類別
{
public string Name { get; set; } = ""; // 專案名稱
public string Description { get; set; } = ""; // 專案描述
public string TargetAudience { get; set; } = ""; // 目標使用者
public string ProblemToSolve { get; set; } = ""; // 要解決的問題
public List<string> CoreFeatures { get; set; } = new(); // 核心功能清單
}
// 範例:電商網站的專案想法 // Example: e-commerce project idea
var idea = new ProjectIdea // 建立電商專案想法
{
Name = "小農直售平台", // 設定專案名稱
Description = "讓小農可以直接賣農產品給消費者", // 設定專案描述
TargetAudience = "小農 + 注重食安的消費者", // 設定目標族群
ProblemToSolve = "中間商抽太多,小農沒賺到錢", // 設定要解決的問題
CoreFeatures = new List<string> // 列出核心功能
{
"商品上架管理", // 功能一:商品管理
"購物車與結帳", // 功能二:購物流程
"訂單追蹤", // 功能三:訂單管理
"會員系統" // 功能四:會員功能
}
};
User Story 撰寫法
💡 比喻:點菜單 User Story 就像餐廳的菜單——寫清楚「誰要吃什麼」以及「為什麼想吃」。 格式固定:身為(角色),我想要(功能),以便(目的)
User Story 範本
// 定義 User Story 類別 // Define User Story class
public class UserStory // 使用者故事類別
{
public string Role { get; set; } = ""; // 角色(誰在用)
public string Want { get; set; } = ""; // 想要什麼功能
public string SoThat { get; set; } = ""; // 目的是什麼
public string Priority { get; set; } = ""; // 優先順序
public List<string> AcceptanceCriteria { get; set; } = new(); // 驗收標準
}
// 撰寫 User Story 範例 // Write User Story examples
var stories = new List<UserStory> // 建立使用者故事清單
{
new UserStory // 第一個故事:商品瀏覽
{
Role = "消費者", // 角色是消費者
Want = "瀏覽商品清單並篩選分類", // 想要瀏覽和篩選商品
SoThat = "快速找到想買的農產品", // 目的是快速找到商品
Priority = "Must", // 優先順序:必須有
AcceptanceCriteria = new List<string> // 驗收標準清單
{
"能顯示所有商品的卡片式列表", // 標準一:卡片式顯示
"能按分類篩選(蔬菜、水果、米糧)", // 標準二:分類篩選
"能按價格排序(高到低、低到高)", // 標準三:價格排序
"每頁顯示 12 筆,有分頁功能" // 標準四:分頁功能
}
},
new UserStory // 第二個故事:購物車
{
Role = "消費者", // 角色是消費者
Want = "將商品加入購物車並調整數量", // 想要管理購物車
SoThat = "一次購買多樣商品", // 目的是批量購買
Priority = "Must", // 優先順序:必須有
AcceptanceCriteria = new List<string> // 驗收標準清單
{
"點擊加入購物車後顯示成功提示", // 標準一:加入回饋
"購物車頁面可修改數量", // 標準二:修改數量
"能顯示小計與總計金額", // 標準三:金額計算
"能刪除購物車中的商品" // 標準四:刪除功能
}
}
};
// 輸出 User Story 格式 // Output formatted User Story
foreach (var story in stories) // 逐一輸出每個故事
{
var output = $"身為 {story.Role}," + // 組合角色部分
$"我想要 {story.Want}," + // 組合功能部分
$"以便 {story.SoThat}"; // 組合目的部分
Console.WriteLine(output); // 印出完整的 User Story
Console.WriteLine($"優先順序:{story.Priority}"); // 印出優先順序
}
功能拆解與優先排序 (MoSCoW)
💡 比喻:搬家打包 搬家時你會分類:一定要帶的(身分證、錢包)、應該帶的(換洗衣物)、 可以帶的(書本)、不帶的(舊雜誌)。 MoSCoW 就是幫功能做一樣的分類。
// 定義 MoSCoW 優先順序列舉 // Define MoSCoW priority enum
public enum MoSCoWPriority // MoSCoW 優先順序
{
Must, // 必須有:沒有這個系統不能用 // Must have
Should, // 應該有:很重要但可以晚一點做 // Should have
Could, // 可以有:有的話更好 // Could have
Wont // 不做:這次不做,未來再說 // Won't have
}
// 定義功能項目類別 // Define feature item class
public class FeatureItem // 功能項目類別
{
public string Name { get; set; } = ""; // 功能名稱
public MoSCoWPriority Priority { get; set; } // MoSCoW 優先等級
public int EstimatedDays { get; set; } // 預估天數
public string Note { get; set; } = ""; // 備註說明
}
// 電商網站的功能拆解 // Feature breakdown for e-commerce
var features = new List<FeatureItem> // 建立功能清單
{
// Must(必須有) // 沒這些就不能上線
new() { Name="會員註冊登入", Priority=MoSCoWPriority.Must, EstimatedDays=3, Note="用 Identity Framework" }, // 會員系統
new() { Name="商品 CRUD", Priority=MoSCoWPriority.Must, EstimatedDays=4, Note="含圖片上傳" }, // 商品管理
new() { Name="購物車", Priority=MoSCoWPriority.Must, EstimatedDays=3, Note="Session + DB 雙軌" }, // 購物車功能
new() { Name="訂單建立", Priority=MoSCoWPriority.Must, EstimatedDays=3, Note="含庫存扣減" }, // 訂單功能
// Should(應該有) // 第一版之後盡快做
new() { Name="商品搜尋", Priority=MoSCoWPriority.Should, EstimatedDays=2, Note="關鍵字 + 分類" }, // 搜尋功能
new() { Name="Email 通知", Priority=MoSCoWPriority.Should, EstimatedDays=2, Note="訂單確認信" }, // 通知功能
new() { Name="訂單狀態追蹤", Priority=MoSCoWPriority.Should, EstimatedDays=2, Note="狀態機設計" }, // 追蹤功能
// Could(可以有) // 有時間再做
new() { Name="商品評價", Priority=MoSCoWPriority.Could, EstimatedDays=3, Note="星等 + 文字評論" }, // 評價功能
new() { Name="推薦系統", Priority=MoSCoWPriority.Could, EstimatedDays=5, Note="基於購買紀錄" }, // 推薦功能
// Won't(不做) // 這次不做
new() { Name="即時聊天", Priority=MoSCoWPriority.Wont, EstimatedDays=7, Note="改用 LINE 客服" }, // 聊天功能
};
// 統計各優先級的工作天數 // Calculate total days per priority
var summary = features // 從功能清單開始
.GroupBy(f => f.Priority) // 依優先順序分組
.Select(g => new // 建立統計物件
{
Priority = g.Key, // 優先等級
Count = g.Count(), // 功能數量
TotalDays = g.Sum(f => f.EstimatedDays) // 總天數
})
.OrderBy(s => s.Priority); // 依優先順序排序
foreach (var item in summary) // 逐一輸出統計結果
{
Console.WriteLine($"{item.Priority}: {item.Count} 個功能,共 {item.TotalDays} 天"); // 印出統計
}
資料庫 Schema 設計流程
💡 比喻:收納箱貼標籤 設計 Schema 就像整理房間——先決定要幾個收納箱(Table), 每個箱子貼上標籤(Column),箱子之間用線連起來(Relationship)。
// 設計電商網站的核心 Entity // Design core entities for e-commerce
// 第一步:識別核心實體 // Step 1: Identify core entities
public class Product // 商品實體
{
public int Id { get; set; } // 主鍵
public string Name { get; set; } = ""; // 商品名稱
public string Description { get; set; } = ""; // 商品描述
public decimal Price { get; set; } // 售價
public int Stock { get; set; } // 庫存數量
public int CategoryId { get; set; } // 外鍵:分類 ID
public Category Category { get; set; } = null!; // 導覽屬性:分類
public DateTime CreatedAt { get; set; } // 建立時間
public bool IsActive { get; set; } = true; // 是否上架
}
public class Category // 分類實體
{
public int Id { get; set; } // 主鍵
public string Name { get; set; } = ""; // 分類名稱
public string Slug { get; set; } = ""; // 網址用的代稱
public List<Product> Products { get; set; } = new(); // 導覽屬性:此分類的商品
}
public class Order // 訂單實體
{
public int Id { get; set; } // 主鍵
public string UserId { get; set; } = ""; // 會員 ID
public DateTime OrderDate { get; set; } // 訂單日期
public decimal TotalAmount { get; set; } // 訂單總金額
public string Status { get; set; } = "Pending"; // 訂單狀態
public List<OrderItem> Items { get; set; } = new(); // 導覽屬性:訂單明細
}
public class OrderItem // 訂單明細實體
{
public int Id { get; set; } // 主鍵
public int OrderId { get; set; } // 外鍵:訂單 ID
public int ProductId { get; set; } // 外鍵:商品 ID
public int Quantity { get; set; } // 購買數量
public decimal UnitPrice { get; set; } // 當時單價(記錄歷史價格)
public Order Order { get; set; } = null!; // 導覽屬性:所屬訂單
public Product Product { get; set; } = null!; // 導覽屬性:商品資訊
}
Entity Relationship 關係圖
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
│ Category │────<│ Product │>────│ OrderItem │
│ │ 1:N │ │ 1:N │ │
│ Id │ │ Id │ │ Id │
│ Name │ │ Name │ │ OrderId (FK) │
│ Slug │ │ Price │ │ ProductId(FK)│
└─────────────┘ │ CategoryId │ │ Quantity │
│ Stock │ │ UnitPrice │
└──────────────┘ └──────┬───────┘
│ N:1
┌──────┴───────┐
│ Order │
│ Id │
│ UserId │
│ TotalAmount │
│ Status │
└──────────────┘
API 端點規劃 (RESTful)
// RESTful API 端點規劃表 // RESTful API endpoint planning
// 規則:用名詞不用動詞,用 HTTP Method 區分操作 // Use nouns, not verbs
public class ApiEndpoint // API 端點類別
{
public string Method { get; set; } = ""; // HTTP 方法
public string Path { get; set; } = ""; // 路徑
public string Description { get; set; } = ""; // 說明
public string Auth { get; set; } = ""; // 驗證需求
}
var endpoints = new List<ApiEndpoint> // 建立端點清單
{
// 商品相關 API // Product endpoints
new() { Method="GET", Path="/api/products", Description="取得商品清單(支援分頁、篩選)", Auth="無" }, // 公開取得商品
new() { Method="GET", Path="/api/products/{id}", Description="取得單一商品詳情", Auth="無" }, // 公開取得商品詳情
new() { Method="POST", Path="/api/products", Description="新增商品", Auth="Admin" }, // 管理員新增商品
new() { Method="PUT", Path="/api/products/{id}", Description="更新商品資訊", Auth="Admin" }, // 管理員更新商品
new() { Method="DELETE", Path="/api/products/{id}", Description="刪除商品", Auth="Admin" }, // 管理員刪除商品
// 購物車相關 API // Cart endpoints
new() { Method="GET", Path="/api/cart", Description="取得購物車內容", Auth="User" }, // 會員取得購物車
new() { Method="POST", Path="/api/cart/items", Description="加入商品到購物車", Auth="User" }, // 會員加入商品
new() { Method="PUT", Path="/api/cart/items/{id}", Description="更新購物車商品數量", Auth="User" }, // 會員修改數量
new() { Method="DELETE", Path="/api/cart/items/{id}", Description="移除購物車商品", Auth="User" }, // 會員移除商品
// 訂單相關 API // Order endpoints
new() { Method="POST", Path="/api/orders", Description="建立訂單(從購物車結帳)", Auth="User" }, // 會員下單
new() { Method="GET", Path="/api/orders", Description="取得我的訂單列表", Auth="User" }, // 會員查詢訂單
new() { Method="GET", Path="/api/orders/{id}", Description="取得訂單詳情", Auth="User" }, // 會員查詢訂單詳情
};
// 印出 API 規劃表 // Print API plan
foreach (var ep in endpoints) // 逐一輸出端點
{
Console.WriteLine($"{ep.Method,-8} {ep.Path,-30} {ep.Description}"); // 格式化輸出
}
線框圖 (Wireframe) 工具推薦
// 線框圖工具比較 // Wireframe tool comparison
var tools = new Dictionary<string, string> // 建立工具字典
{
["Figma"] = "免費版功能強大,支援團隊協作,業界標準", // Figma:最推薦
["Excalidraw"] = "免費開源,手繪風格,適合快速草圖", // Excalidraw:快速草圖
["draw.io"] = "免費,適合畫流程圖和架構圖", // draw.io:流程圖
["Balsamiq"] = "付費,低保真度線框圖專用", // Balsamiq:專業線框圖
["紙和筆"] = "最快!先在紙上畫,確認後再用工具" // 紙筆:最快速
};
foreach (var tool in tools) // 逐一輸出工具資訊
{
Console.WriteLine($"🔧 {tool.Key}: {tool.Value}"); // 印出工具名稱和說明
}
時程估算技巧
💡 比喻:做菜估時間 你覺得炒盤菜 10 分鐘,但你忘了算洗菜、切菜、洗鍋子的時間。 程式估時也一樣——寫 code 只佔 30%,測試、除錯、調整佔 70%。
// 時程估算工具類別 // Time estimation utility class
public class TaskEstimator // 任務估算器
{
public string TaskName { get; set; } = ""; // 任務名稱
public double OptimisticDays { get; set; } // 樂觀估計(天)
public double PessimisticDays { get; set; } // 悲觀估計(天)
public double MostLikelyDays { get; set; } // 最可能估計(天)
// PERT 估算法:(樂觀 + 4*最可能 + 悲觀) / 6 // PERT estimation formula
public double PertEstimate => // 計算 PERT 估計值
(OptimisticDays + 4 * MostLikelyDays + PessimisticDays) / 6; // 加權平均公式
}
// 使用 PERT 估算法 // Apply PERT estimation
var tasks = new List<TaskEstimator> // 建立任務清單
{
new() { TaskName="會員系統", OptimisticDays=2, MostLikelyDays=3, PessimisticDays=7 }, // 估算會員系統
new() { TaskName="商品 CRUD", OptimisticDays=3, MostLikelyDays=4, PessimisticDays=8 }, // 估算商品功能
new() { TaskName="購物車", OptimisticDays=2, MostLikelyDays=3, PessimisticDays=6 }, // 估算購物車
new() { TaskName="訂單流程", OptimisticDays=3, MostLikelyDays=5, PessimisticDays=10 }, // 估算訂單功能
new() { TaskName="前端頁面", OptimisticDays=5, MostLikelyDays=7, PessimisticDays=14 }, // 估算前端開發
new() { TaskName="測試與修正", OptimisticDays=3, MostLikelyDays=5, PessimisticDays=10 }, // 估算測試時間
};
double totalDays = 0; // 初始化總天數
foreach (var task in tasks) // 逐一計算每個任務
{
var estimate = Math.Ceiling(task.PertEstimate); // 無條件進位取整數天
totalDays += estimate; // 累加到總天數
Console.WriteLine($"{task.TaskName}: 約 {estimate} 天"); // 印出估算結果
}
// 加上緩衝時間(建議加 20-30%) // Add buffer time (20-30% recommended)
var buffer = Math.Ceiling(totalDays * 0.25); // 加 25% 緩衝
Console.WriteLine($"預估總天數:{totalDays} 天 + 緩衝 {buffer} 天 = {totalDays + buffer} 天"); // 印出最終估計
黃金法則:初學者把你的估計乘以 2,中級乘以 1.5,資深乘以 1.2。
🤔 我這樣寫為什麼會錯?
❌ 錯誤 1:沒有寫驗收標準
// ❌ 錯誤:User Story 太模糊 // Mistake: vague User Story
var badStory = "使用者可以買東西"; // 這太模糊了,什麼叫買東西?
// ✅ 正確:要有明確的驗收標準 // Correct: include acceptance criteria
var goodStory = new UserStory // 建立清楚的使用者故事
{
Role = "消費者", // 明確的角色
Want = "用信用卡結帳購物車的商品", // 明確的功能
SoThat = "快速完成購買流程", // 明確的目的
AcceptanceCriteria = new List<string> // 列出可驗證的標準
{
"支援 Visa/MasterCard", // 驗收條件一
"結帳失敗要顯示錯誤訊息", // 驗收條件二
"成功後寄確認信" // 驗收條件三
}
};
❌ 錯誤 2:所有功能都標 Must
// ❌ 錯誤:每個功能都是 Must // Mistake: everything is Must priority
var badPriority = new List<FeatureItem> // 全部都標 Must 等於沒排序
{
new() { Name="AI 推薦", Priority=MoSCoWPriority.Must, EstimatedDays=10 }, // AI 推薦不是第一版必須
new() { Name="多國語言", Priority=MoSCoWPriority.Must, EstimatedDays=5 }, // 多語言也不是第一版必須
new() { Name="基本登入", Priority=MoSCoWPriority.Must, EstimatedDays=2 }, // 這才是真正的 Must
};
// 當所有功能都是 Must,就等於沒有優先排序 // If everything is Must, nothing is Must
// 問自己:「沒有這個功能,系統能不能上線?」 // Ask: can the system launch without this?
❌ 錯誤 3:忘記記錄歷史價格
// ❌ 錯誤:訂單明細只記商品 ID // Mistake: only storing product ID in order
public class BadOrderItem // 糟糕的訂單明細設計
{
public int ProductId { get; set; } // 只存商品 ID
public int Quantity { get; set; } // 只存數量
// 問題:商品漲價後,舊訂單金額也跟著變! // Bug: old orders change when price updates!
}
// ✅ 正確:記錄當時的價格快照 // Correct: store price snapshot
public class GoodOrderItem // 正確的訂單明細設計
{
public int ProductId { get; set; } // 商品 ID
public int Quantity { get; set; } // 數量
public decimal UnitPrice { get; set; } // 下單當時的單價(快照)
public string ProductName { get; set; } = ""; // 下單當時的商品名稱(快照)
}
📋 本章重點
| 步驟 | 做什麼 | 產出物 |
|---|---|---|
| 1 | 寫下想法 | 專案企劃書 |
| 2 | 寫 User Story | 使用者故事清單 |
| 3 | MoSCoW 排序 | 功能優先排序表 |
| 4 | 設計 Schema | ER 圖 + Migration |
| 5 | 規劃 API | API 端點文件 |
| 6 | 畫 Wireframe | 線框圖 |
| 7 | 估算時程 | 甘特圖 / 時程表 |
🎯 下一步:拿著這份規劃書,我們開始實作電商網站!