再讀整潔架構(Clean Architecture)

Rex Wang
7 min readJul 27, 2020

--

最近因為某些因素,我重讀了Uncle Bob 的 Clean Architecture,找尋解決問題的靈感。大師的書裡總是有微言大義,也許是我的理解力太差,哈。

何謂架構?為什麼需要架構設計?

我對於軟體架構設計的興趣,來自於歷經幾個慘烈的專案後,我開始思考有沒有什麼方式可以讓專案進行得順利一些?系統維護與變更也可以更容易一些?因為長年開發工作的關係,很多機會跟客戶、同事討論到系統設計,總是著重在”如何在時程與預算內完成需求”;敏捷是我其他戰友走的一條路,架構設計則是我覺得可行的另一條路,也成為我這幾年來尋尋覓覓的方向。敏捷當然也要有架構設計,但不是每個人都這樣想,不過,那又是另外一個故事了。

Uncle Bob 在書中定義了軟體架構的目標:軟體架構的目標是最小化建置和維護「需求系統」所需要的人力資源。

以我個人世俗的說法就是,如何以最快的速度、最少的人力與經費負擔完成專案(全新或是需求變更都算),大家可以平安回家,就是軟體架構的目標XD。但通常,我們在有限的時間、資源與人力情況下,僅僅只能想到如何按照規劃準時上線,甚至品質與預算都沒辦法被詳細評估,甚至犧牲,更不可能想到,未來如何發展這個滾燙的山芋。

Uncle Bob提到軟體架構,除了需要考慮設計與開發過程的相關議題,部署、維運,甚至後續的需求變更,都應該被納入思考與規劃。讓我想起,曾經負責的十年系統,每次需求變更的上線工作,都是驚濤駭浪的過程;也想到,如果微服務的部署工作,沒有經過妥善規劃與方便的工具,勢必會是一場災難,這也是另外一個大議題,改天再聊。

Clean Architecture 的目標

Alistair Cockburn開發的六角形架構(或是被稱為 Port 與Adapters),在 Chris Richardson 的 Microservices Patterns 與 Vaughn Vernon 的 Implementing Domain-Driven Design 都有說明,兩位大師都推薦以這樣的架構來安排系統或是微服務;同樣的架構也出現在 Netflix 的 TechBlog。而Clean Architecture 與六角形架構僅有些 少許實作細節上的差異,其目標都是一樣的。

圖片來源:Netflix TechBlog

書中提到這樣的架構,有獨立於框架、可測試、獨立於UI、獨立於資料庫等特徵。Uncle Bob建議系統有四層,內圈為策略,外圈為機制;內圈為核心,外圈為細節;內圈為高層,外圈為低層。開發方向可以由內而外,相依方向則應該是由外而內,也就是系統核心不需要知道在外圈的資料庫、框架與UI,讓軟體可以真正變得更軟,更易於改變。因此轉換層是Clean Architecure的關鍵,也是一般讀者最想理解的實作內容。

大家常見的專案開發方式,通常習慣先確定好報表產出、前端技術,接著開始思考資料庫設計,才開始著手進行開發,不知不覺或是根本就是刻意的把系統核心與週邊綁定。在 Clean Architecture的建議方式:相依的方向是由外而內、由IO而核心;開發與設計則可以用事件風暴(Event Storming) 、使用者故事對照(User Story Mapping)或是OOAD等方式找出 Use Cases 與 Entities。外層的框架、展示層與資料庫則可稍後決定。兩種方式最大的差異,會是在未來系統演進時,Clean Architecture 會獲的極大的彈性與好處,更可保持核心的獨立性,不受到外層增加或變更的影響。

SOLID 設計原則

SOLID設計原則包含:單一職責原則(Single responsibility principle, SRP)、開放封閉原則(Open-Close principle, OCP)、里氏替換原則(Liskov substitution principle, LSP)、接口隔離原則(Interface segregation principle, ISP)、反向相依原則(Dependency inversion principle, DIP)等五個。這個物件導向設計原則並非由Uncle Bob 獨創,但在Clean Code 系列書籍裡,提到 SOLID 設計原則不止一次,且在Clean Architecture裡面扮演著非常重要的角色。

其中,反向相依原則即是讓內外圈解偶的關鍵技巧。過去我們習慣將Entity 與 Repository 放在同一層(或是同一個 package),在 Clean Architecture 則做了一個調整,也運用到GoF提到的”針對介面寫程式”,上層提供了一個 Repository的Interface,再由低層實作真正對應的資料庫存取類別。在資料庫尚未決定前,即可使用程式語言基礎的資料結構,代替資料庫,進行核心功能開發與測試,一旦決定資料庫種類與廠牌,即可進行調整,並不會影響完成的核心功能。也以此達成反向相依。

反向相依

單一責任原則簡單來說就是,一個類別的改變只能有一個理由,換句話說:如果類別A同時被另外兩個類別使用,另外兩個類別的改變,極有可能互相影響,以實務上來說,就是要不斷的測試以避免發生問題;但如果以單一責任原則更是一種釜底抽薪的設計方式。

單一責任原則

但是如果有一些服務就是需要放在同一個類別,該如何處理呢?這時候就需要用到Facade Pattern了。(Design Pattern 部分,我們也留待以後XD)建構微服務的作者 Sam Newman 提出的 Backend for Frontend 的設計模式,則是充分了運用這個概念。

框架的要與不要

身為Spring Framework的熱愛者與Spring 團隊之友,我還是必須認同Uncle Bob的觀點:框架不是架構。雖然在台上表演用Spring Boot ,似乎可以很快開發出微服務,但表演只是強化印象,系統設計還是需要一些深思熟慮。框架不應該深入整個應用的核心,但是如果沒有框架,許多功能要靠自己完成,還要達到一定的品質要求,將會是一個不可能的任務。

很多以Spring Boot建構微服務的Demo 都是從 https://start.spring.io開始,要建立起一個Restful的API就在彈指之間,但如果是我的專案,我不會以這樣的方式開始我的設計。而會確立Entities與Use Cases兩層,也就是實際上系統行為開始,逐步的往外開發;核心與對外的API,則以Adapter Pattern的方式對接,以後如果有更好的通訊方式、應用框架,則保有足夠的彈性進行修改。

結論

我個人非常喜歡且推崇的泰迪曾說過:很多軟體工程或是說敏捷方法和敏捷實務做法都是意圖讓軟體變軟,例如OO、OOAD、DDD、TDD/BDD/ATDD/SBE、Clean Architecture、Patterns、CI/CD、UT、Refactoring、SOLID還有XP、Scrum、Lean、Kanban等。變軟是為了更容易改變,偏偏大家總以為自己手上的系統永遠不會改變;如果想像自己是達文西,手上的系統是聖彼得大教堂,那敲敲打打、修修改改,假裝自己是在追尋”建築的永恆之道”,心情應該會好多了。

--

--

Rex Wang

最近看了一些網路技術大神建議,藉由寫部落格來記錄自己的學習歷程,並且可以累積自己的技術能力。於是,繼多年前雜誌技術編輯生涯,我再度展開了屬於自己的技術寫作之旅。