Codebase Design
設計深度模組:將大量行為隱藏在一個小型介面之後,並將其置於呼叫者與測試可以相遇的接縫處。目標是為呼叫者提供槓桿效應,為維護者提供局部性,並讓測試能夠在重構中存活。
規則
- 深度是介面的屬性,而非實作的屬性。 一個深度模組可以由可互換的小零件構成,只是這些零件不屬於它的介面。它可以有自己的內部接縫供其測試使用,以及在其介面處的外部接縫。
- 執行刪除測試。 想像刪除這個模組。如果複雜度隨之消失,那它就是一個傳遞層。如果複雜度散佈到呼叫者中,那它就證明了自己的存在價值。
- 介面就是測試表面。 呼叫者與測試跨越相同的接縫。如果你需要繞過介面進行測試,那模組的形狀可能不對。
- 一個轉接器代表假設的接縫;兩個才讓它成真。 只在實際會變化的地方加入接縫,通常是生產環境與測試環境。
- 縮小介面。 更少的方法、更簡單的參數、更多隱藏在內部的複雜度。
- 接受依賴而非建立依賴。 將支付閘道作為參數傳入,而不是在函數內部建構它。
- 在設計允許的情況下,回傳結果而非產生副作用。
詞彙
請精確使用以下術語。不要用「component」、「service」、「API」或「boundary」替代。
- Module(模組): 任何擁有介面與實作的東西,無論規模大小:函數、類別、套件,或跨層級的切片。
- Interface(介面): 呼叫者為了正確使用模組所需知道的一切:型別、不變量、順序、錯誤模式、必要配置與效能。不僅僅是簽名。
- Implementation(實作): 模組內部的程式碼。一個小轉接器可以有大型的實作(例如 Postgres 儲存庫),而一個大型轉接器可以有小型實作(例如記憶體中的假物件)。
- Depth(深度): 介面處的槓桿效應,指的是呼叫者或測試每學習一個單位的介面所能運用的行為量。當一個模組有大量行為隱藏在一個小型介面背後時稱為深度模組,當介面幾乎與實作一樣複雜時稱為淺層模組。
- Seam(接縫):一個可以改變行為而無需編輯該處的地方;也就是模組介面所在的位置。選擇接縫本身就是一個獨立的決策。
- Adapter(轉接器): 一個具體的東西,用來在接縫處填入一個介面。它命名一個角色,而非內容。
- Leverage(槓桿效應): 呼叫者從深度中獲得的效益。一個實作在多個呼叫點與測試中獲得回報。
- Locality(局部性): 維護者從深度中獲得的效益。變更、錯誤、知識與驗證都集中在同一個地方。
被排除的框架:將深度視為實作行數與介面行數的比例,因為這會鼓勵灌水;將「介面」僅視為型別關鍵字或公開方法,因為這太狹隘;使用「boundary」,因為它會與 DDD 的 bounded context 衝突。
如果專案保留了 CONTEXT.md 詞彙表,請在這些術語的基礎上使用該領域的術語。
深入探討
- 若要按依賴類型加深一組淺層模組,請閱讀 DEEPENING.md。
- 若要比較多個截然不同的介面,請閱讀 DESIGN-IT-TWICE.md。它會啟動三個或以上的子代理,所以僅在使用者要求時才使用。