[{"data":1,"prerenderedAt":241},["ShallowReactive",2],{"blog-\u002Fblog\u002Fwhere-code-place\u002F":3},{"id":4,"title":5,"body":6,"category":219,"date":220,"description":221,"extension":222,"meta":223,"navigation":230,"ogImage":231,"path":232,"robots":233,"schemaOrg":234,"seo":237,"sitemap":238,"stem":239,"__hash__":240},"content\u002Fblog\u002Fwhere-code-place.md","我有在思考程式碼到底該放哪裡",{"type":7,"value":8,"toc":206},"minimark",[9,13,18,21,24,31,37,40,44,60,63,71,75,78,81,84,92,95,98,101,104,109,119,123,129,140,144,147,173,179,183,186,189,192,195,198,201],[10,11,12],"p",{},"這篇文章不是要介紹完整的 Feature-based Design，而是記錄我看完一場分享後，重新理解自己過去在專案中做過的檔案拆分與模組設計。若你有不同的實務經驗，也歡迎交流。",[14,15,17],"h2",{"id":16},"為什麼要拆分程式碼","為什麼要拆分程式碼？",[10,19,20],{},"一開始，我也不太清楚為什麼要把程式碼拆開。心裡常想：「拆成這麼多檔案，維護時不是得跳來跳去地看嗎？一個檔案不能把事情都做完嗎？」",[10,22,23],{},"直到某個畫面同時包含彈出視窗、依按鈕切換的表單，以及依狀態變化的內容，卻全都塞在同一個檔案裡，我才發現問題。出了錯時，光是找到問題所在就得花不少時間。",[10,25,26,27],{},"把程式碼依用途拆成不同檔案後，發生問題時，我可以直接從相關檔案開始找，不必先讀過一大段與這次修改無關的內容。",[28,29,30],"mark",{},"拆分能縮小需要理解與檢查的範圍。",[10,32,33,34],{},"更抽象地說，判斷要不要拆分時，與其只問「這段程式碼長不長」，",[28,35,36],{},"不如問：「這些程式碼的責任邊界在哪裡？」",[10,38,39],{},"例如，驗證規則可能會隨商業規則改變；而「什麼狀態下啟用驗證」則可能隨流程改變。兩者的變更原因不同，把它們分開就有意義。",[41,42,43],"h3",{"id":43},"好處",[45,46,47,54],"ul",{},[48,49,50,53],"li",{},[28,51,52],{},"修改範圍更明確：","把驗證規則與套用時機分開後，調整驗證規則時，只需要修改負責規則的檔案，不必連帶處理流程。",[48,55,56,59],{},[28,57,58],{},"更容易與 AI 協作：","每個檔案的責任清楚，就能把修改範圍限制在特定模組，例如只處理 validation，不必讓 AI 同時理解一個幾千行的 component，也能降低提供 context 的成本。",[41,61,62],{"id":62},"需要留意的地方",[45,64,65,68],{},[48,66,67],{},"**理解檔案關係需要時間：**其他人接手維護時，可能得先了解各檔案如何組合、彼此如何協作。",[48,69,70],{},"**避免為拆而拆：**如果拆分沒有讓責任更清楚，反而增加跳轉與理解成本，就值得重新考慮。",[14,72,74],{"id":73},"我怎麼做","我怎麼做？",[10,76,77],{},"我在公司的專案中也有實踐這種做法，從整體專案到單一模組都有一些嘗試。雖然還稱不上完整的方法，但在後續維護時，我確實感受到它帶來的好處。",[41,79,80],{"id":80},"整個專案",[10,82,83],{},"整體而言，專案最外層仍以 Technical-based 的方式分類；在既有資料夾中，我再依模組細分，並依不同職責拆開檔案。這樣的做法有些接近 Feature-based 的組織方式。",[10,85,86,87,91],{},"以前 ",[88,89,90],"code",{},"pages\u002F"," 底下只有主題分類，沒有再細分各個模組，頁面檔案散落在不同主題資料夾中，不容易一眼找到負責呈現某個畫面的檔案。",[10,93,94],{},"後來，我改為替各個畫面建立資料夾，再依單一模組整理相關程式碼。",[41,96,97],{"id":97},"單一模組",[10,99,100],{},"以訂單功能為例，畫面可能依不同狀態顯示不同表單；狀態也會決定欄位是可填寫，還是只供檢視。",[10,102,103],{},"這些表單、初始化 API、驗證邏輯和送出流程，原本都可以寫在同一個檔案裡。畫面照樣能運作，只是檔案可能逐漸膨脹到幾千行，讓後續理解與修改變得困難。",[105,106,108],"h4",{"id":107},"before","Before",[110,111,117],"pre",{"className":112,"code":114,"language":115,"meta":116},[113],"language-text","pages\u002F\n└── OrderPage.vue\n","text","",[88,118,114],{"__ignoreMap":116},[105,120,122],{"id":121},"after","After",[110,124,127],{"className":125,"code":126,"language":115,"meta":116},[113],"order\u002F\n├── OrderPage.vue\n├── components\u002F\n│   ├── DraftForm.vue\n│   ├── ReviewForm.vue\n│   └── CompletedForm.vue\n└── composables\u002F\n    ├── useOrderInit.ts\n    ├── useOrderSubmit.ts\n    └── useOrderValidation.ts\n",[88,128,126],{"__ignoreMap":116},[10,130,131,132,135,136,139],{},"我先依照邏輯與畫面拆分出 ",[88,133,134],{},"composables\u002F","、",[88,137,138],{},"components\u002F"," 和主程式。主程式則負責把這些部分組合起來。",[105,141,143],{"id":142},"composables","Composables",[10,145,146],{},"我依照不同功能拆出幾個 composable：",[45,148,149,155,161,167],{},[48,150,151,154],{},[88,152,153],{},"init","：依進入畫面時的狀態，決定要呼叫哪支初始化 API。",[48,156,157,160],{},[88,158,159],{},"submit","：依目前狀態，決定呼叫哪支 API 送出表單。",[48,162,163,166],{},[88,164,165],{},"validate","：依目前狀態，決定套用哪些驗證規則。",[48,168,169,172],{},[88,170,171],{},"main","：管理共用變數與 API。",[10,174,175,176,178],{},"現在回頭看，",[88,177,171],{}," 的責任或許還能再拆得更清楚；以目前的需求來說，則暫時沒有必要。",[105,180,182],{"id":181},"components","Components",[10,184,185],{},"我把不同狀態的表單拆成各自的 component，讓它們盡量只處理表單欄位與畫面呈現，不直接改變流程。流程相關的決策則交由主程式負責。",[105,187,188],{"id":188},"主程式",[10,190,191],{},"主程式負責組合 composables 與 components，決定目前狀態下要呈現哪個 UI，以及使用者操作後如何進入下一個流程。具體的驗證、資料轉換與 API 操作，則交給各自負責的模組。",[14,193,194],{"id":194},"結論",[10,196,197],{},"我目前的體會是，設計檔案結構真正要解決的，並不是替每段程式碼找到唯一正確的位置，而是透過結構，清楚表達程式的職責、邊界與依賴關係。",[10,199,200],{},"好的結構應該讓人看得出：這段程式碼為什麼放在這裡、哪些內容通常需要一起修改，以及哪些部分不應彼此依賴。團隊也需要能用一致的方式理解這些安排。",[10,202,203],{},[28,204,205],{},"如果一種結構能降低理解與修改程式碼的成本，那麼對目前的團隊與專案而言，它就是有價值的結構。",{"title":116,"searchDepth":207,"depth":207,"links":208},2,[209,214,218],{"id":16,"depth":207,"text":17,"children":210},[211,213],{"id":43,"depth":212,"text":43},3,{"id":62,"depth":212,"text":62},{"id":73,"depth":207,"text":74,"children":215},[216,217],{"id":80,"depth":212,"text":80},{"id":97,"depth":212,"text":97},{"id":194,"depth":207,"text":194},"Frontend","2026-08-20","無意間發現，自己做的事情其實有點 Feature-based","md",{"tags":224,"headline":226,"author":227,"datePublished":220},[225,219],"Vue3","Feature-based",{"@type":228,"name":229},"Person","Jamespig",true,{"url":116},"\u002Fblog\u002Fwhere-code-place",null,[235],{"@type":236},"TechArticle",{"title":5,"description":221},{"loc":232,"lastmod":220},"blog\u002Fwhere-code-place","Y3k6UW6m4F1Z_Ooxft9vJ0BoAv9oVmDQd7uxCBAwMEk",1793778789448]