工作上事情總是多到做不完,要怎麼把有限的時間花在最關鍵的部分,是每位工程師都得面對的課題。對於後端工程師而言,Clean Architecture(簡潔架構)就是一套幫助我們把「事情擺在對的位置」的設計方針。

Kuma 自承不是擅長架構設計的人,但實際導入 Clean Architecture 之後感受非常明顯:分層整理乾淨後,測試好寫、重構也有依據,這份系列文章想推廣的就是這份體會。

簡介#

Clean Architecture 由 Robert C. Martin(鮑伯大叔)於 2017 年的同名書中提出。在這之前已有 Hexagonal Architecture、Onion Architecture 等多種知名架構,鮑伯大叔觀察到這些架構雖然形式不同,卻共享一些重要的特質:

  • 與框架解耦:框架是工具,必須隔離於核心邏輯之外,才不會被工具反過來限制設計。
  • 可測試性:核心邏輯應獨立於 UI、資料庫、網路等細節之外,使這些細節可以隨意抽換以利測試。
  • 與資料庫無關:把 Oracle 換成 MySQL、MongoDB 或任何儲存方案,業務邏輯都不該感受到變化。

分層#

Clean Architecture 把系統劃分為四層,但不是上下堆疊,而是由外而內排列;越接近 I/O 越外圍,越靠近核心邏輯越裡面。

Entity#

最內層,負責問題領域中最重要的資料與運算。不論有沒有電腦輔助,這些事情都是必須完成的。

Use Case#

包覆於 Entity 之外,定義在何時該如何使用 Entity 的資料與行為。可以視為「自動化流程」的負責人。

Interface Adapter#

系統的邊界。負責把外部請求翻譯成內層認得的格式,找出合適的 Use Case 執行,再把結果轉換回外部理解的型態。Controller、Repository 多半住在這一層。命名為 Adapter,正因其工作多半是「轉換」。

Frameworks & Drivers#

最外層,包含網路、資料庫、檔案、UI 等所有 I/O 細節。

判斷核心或細節的捷徑是:「離 I/O 越近的越細節,離 I/O 越遠的越核心。」

鮑伯大叔強調 Entity 與 Use Case 是支撐生意的主要推手,必須慎重對待;I/O 雖重要,但屬於可以輕易被抽換的細節。

留個思考題給讀者:Data driven 與 UI driven 的開發方式,搭配 Clean Architecture 又會碰撞出什麼火花?

三大原則#

整體 Clean Architecture 可以濃縮為三條核心原則:

  • 分層原則:所有元件都要分門別類安置,不能亂放。
  • 依賴原則:依賴方向應盡量「由外而內」,內層不應依賴外層。
  • 跨層原則:每一層只能認識同一層或內側相鄰一層的元件,不能跨層直接認識「兩層以外」的元件。

依賴原則的挑戰#

依賴原則禁止由內向外依賴,那當 Use Case 需要操作 Entity,但 Entity 的資料卻儲存在外層的 DB 時該怎麼辦?這時候就要請出依賴反轉原則(Dependency Inversion Principle, DIP)。

DIP 主張業務邏輯不應該依賴具體實作,而應讓兩方共同依賴抽象介面。具體做法是:Use Case 自己定義一個 Repository 抽象介面,介面的實作則放在外層的 Interface Adapter;如此既符合 Clean Architecture 的依賴原則,也滿足實際業務需求。

跨層原則的重要性#

跨層原則執行起來其實有點麻煩。例如 Adapter 層只能認識 Use Case 層,對最內層的 Entity 應該一無所知。即使你想回給前端的物件格式跟 Entity 幾乎一樣,也不能直接回傳,要嘛在 Use Case 轉一次格式,要嘛採用 CQRS(Command Query Responsibility Segregation)模式跳過 Use Case,由同層的 Repository 直接回傳客製化物件。

為什麼這樣麻煩?因為要遵守單一職責原則(SRP)。Entity 應該只關心領域模型最重要的運算與資料,不能被外層需求干擾。如果讓 Adapter 直接存取 Entity,那麼前端格式一動,最核心的 Entity 也跟著動,這正是反模式的典型樣貌。

Kuma 的使用心得#

導入 Clean Architecture 後,元件的擺放有了清楚依據,核心運算邏輯不再依附於資料庫或 UI 細節,整個系統的整潔度與彈性都明顯提升。更重要的是排定工作順序時知道什麼該先做、什麼可以延後決定,做事節奏更穩。

原文出處#

  • 原書/iThome:https://ithelp.ithome.com.tw/articles/10268953