整潔架構只有一條核心規則:原始碼的依賴關係,只能指向內層、指向更高階的策略。內層對外層一無所知——不知道外層的類別、函數,甚至連變數名稱都不知道。抓住這條規則,同心圓要畫成幾層都不是重點;違反它,再漂亮的分層都會腐化。
🧠 Core Ideas
- 依賴規則(The Dependency Rule):原始碼依賴只能向內。內層圓圈中的任何東西,都不知道外層的存在;外層發生的變化,不應波及內層。
- 四層由內而外:Entities(企業級關鍵業務規則)→ Use Cases(應用程式專屬規則)→ Interface Adapters(Controllers、Presenters、Gateways)→ Frameworks & Drivers(資料庫、Web、UI)。越往內越高階、越穩定、離 I/O 越遠。
- Entities vs Use Cases:Entity 承載「即使沒有電腦也存在」的關鍵規則(如銀行利息公式),不依賴任何東西;Use Case 協調 Entity、定義輸入如何轉為輸出,它依賴 Entity,但 Entity 不知道 Use Case 存在。
- 控制流與依賴方向可以相反:控制流常需向外(Use Case 要呼叫 Presenter 顯示結果),但靠 DIP——在內層定義介面、外層去實作——讓原始碼依賴仍然向內。
- 細節都在最外圈:資料庫、Web、框架都是可抽換的插件,而非架構核心。把商業規則與它們解耦,你就能延後決策、獨立測試、自由替換。
- 邊界要畫在「變化軸」上:把「因不同理由、以不同速率改變」的東西分開——商業規則變得慢,UI 與資料庫變得快,就在它們之間畫一條由 DIP 守護的線。
TIP
跨越邊界時,只傳遞簡單、獨立的資料結構(DTO)。千萬別把 Entity 或「資料庫的一行」直接丟給外層 UI——那會讓內層定義洩漏到外層,或迫使內層去遷就外層需求,等於從後門破壞了依賴規則。內外之間永遠隔一層扁平的資料轉換。
⚖️ 為什麼「向內依賴」能換來四種獨立性
依賴規則不是為了畫圖好看,它直接兌換成四個具體的工程能力——這些能力正是「架構價值=易於改變」的實際內容。
整潔架構的四種獨立性
-
獨立於框架。架構不綁死在某個程式庫上;框架是你使用的工具,而不是你必須服從的主人。
-
可測試。業務規則可以在沒有 UI、沒有資料庫、沒有 Web Server 的情況下被單元測試——因為它們不依賴這些東西。
-
獨立於 UI 與資料庫。可以把網頁換成 Console、把 Oracle 換成 Mongo,而完全不動業務規則——因為業務規則根本不在乎資料長怎樣、存在哪。
-
獨立於外部代理。核心業務規則對外面的世界一無所知,這讓它成為系統中最穩定、最不需要被修改的部分。
🖼️ Main 元件:把髒污集中在一個角落
依賴規則有一個現實的裂縫:程式終究得 new 出具體物件才能運作,而 new 一個具體類別就產生了對具體類別的依賴。Uncle Bob 的解法不是消滅它——而是把它集中隔離。
這個「必要的髒污」住在 Main 元件裡。Main 是系統中違反 DIP 的特例:它負責實例化所有的工廠、策略與具體類別,然後把它們交給高層的抽象模組使用。透過抽象工廠(Abstract Factory)模式,Application 只依賴 ServiceFactory 介面,真正建立 ConcreteImpl 的工作被推到 Main 這條系統的最外緣邊界上。於是原始碼依賴與控制流方向相反——控制流從 Main 流向核心,依賴卻從核心指向抽象。結果是:整個系統的其餘部分保持潔淨、只依賴抽象,所有「認識具體細節」的骯髒知識都被關進 Main 這一個可控的角落。這正是「細節指向策略」在啟動流程裡的落實:連系統的入口點,都要向核心的抽象低頭。
IMPORTANT
畫錯邊界,或該畫時不畫,是架構腐化最常見的起點。邊界的紀律本質上就是 SRP 在系統層級的展開——把「為不同角色、不同理由而改變」的東西分開。當你在正確的變化軸上畫線、並用 DIP 守護它,系統就能在需求變動時只動該動的那一塊,而不是牽一髮動全身。
🔑 Takeaways
- 依賴規則只有一條:原始碼依賴永遠指向內層的高階策略,內層對外層一無所知。
- 控制流可以向外,但靠 DIP 讓原始碼依賴始終向內;跨邊界只傳 DTO,別傳 Entity。
- 向內依賴換來四種獨立性:獨立於框架、UI、資料庫與外部代理,且業務規則可獨立測試。
- Main 是必要的髒污:用抽象工廠把「認識具體」的知識集中到系統最外緣。
- 延伸:把依賴規則從類別放大到可部署單位,就是元件的內聚與耦合原則,見 元件內聚與耦合。
No notes yet — jot your takeaways or Q&A here.