古語有云:「歲有凶穰;故谷有貴賤;令有緩急;故物有輕重。」旨在告訴後人,做任何事情,一定要先搞清楚狀況,把事情的優先順序排好再開始動手,方為上策。時間有限,但要做的事就是那麼多,「重要的事先做」講起來容易,做起來真的很難。
接下來的幾天,我們會聊一個近年來,後端工程師或多或少都聽過的主題:「Clean Architecture」。
Clean Architecture 出現後,對筆者的工作影響非常巨大。筆者不算是架構很強的工程師,但正因如此,筆者發現在做後端工作時,只要照著 Clean Architecture 的規劃走,就幾乎不會出什麼亂子。而且在結構乾淨了以後,測試寫起來特別好寫,重構起來也比較有個依據。對此,筆者如獲至寶!
因此筆者在本系列文章中,才特別想要來「野人獻曝」一下,不求能教大家太多專門的知識,但期望有多點人能感受到它的好處,而加以研究,使大家工作能更順利,那我目的就達到了。
Clean Architecture 是 Uncle Bob 在 2017 年出的一本關於「架構」的書。在這之前,出現過很多經典的「系統架構」設計,如 Hexagonal Architecture, Onion Architecture 等。Uncle Bob 認為,這些架構雖各有千秋,但都有一些相似之處:
Uncle Bob 認為,前述這些很棒的架構設計之所以很棒,乃因它們都具備上面的特性。於是綜合這些優點,加上自己的見解,Uncle Bob 在 2017 年提出了「Clean Architecture」這個架構。
Clean Architecture 把系統分成四層,這四層不是由上而下排列,而是由外而內排。 排列的依據為:「越細節的越外面,越核心的越裡面。」這四層分別為:
核心業務、重要資料所在地。不管你在什麼場景,做什麼操作,在你的問題領域中不論有沒有系統幫忙,你都必須得做的事。
這一層包圍在 Entity 之外,定義著對於 Entity 邏輯與資料的使用時機與操作順序,是一個管「自動化」的傢伙。
系統的邊界,對外取得請求內容,轉換成內層認識的樣貌、並尋找適當的 Use Case 來執行任務,最後再轉換成外界認識的樣貌送出去。同時,系統對外發出的請求,也會在這一層轉換。
因為位處系統邊界,做的事大多都是一些轉換的工作,所以命名為 Interface Adapter。
系統中最細節的部分,大部分都是 I/O,舉凡網路、檔案、資料庫、人機介面等,都屬於此範圍。
那麼什麼叫核心,又什麼叫細節呢?實際操作時,其實就是:「離 I/O 越近的越細節,在外層;離 I/O 越遠的越核心,在內層。」
Uncle Bob 說:「Entity 與 Use Case 是你生意能經營的主要推手,必須慎重對待。I/O 雖然有重要,但是是重要的細節,應該要可以簡單被抽換。」
這裡留個思考題:「Data driven 與 UI driven 的開發方式,與 Clean Architecture 搭配使用的話,會發生什麼事呢?」
Clean Architecture 討論了很多設計上的注意事項,而簡言之可以歸納出三大原則:
來思考一下。依賴原則說不能由內往外依賴,那當 Use Case 需要操作 Entity,但 Entity 資料存放在 DB 裡時,應該怎麼辦呢?」裡,就會需要呼叫呼叫物件導向設計的 DIP:Dependency Inversion Principle 登場救援了。
DIP 說,業務邏輯不該依賴資料實作,而應使兩方共同依賴一個抽像介面。
在上面的場景,內層的 Use Case 不該認識外層的 Repository,所以這時 Use Case 應該要定義一個 Repository 抽象介面,而安心的把這個介面的實作放在 Interface Adapters 層,這樣就可以又符合 Clean Architecture 與 DIP,又符合業務需求了。
說到跨層原則,有時候遵守起來挺麻煩的。舉例來說,照 Clean Architecture 的規劃,Adapter 層只可以認識 Use Case 層的物件,它對最內層的 Entity 應該要一無所知。所以,就算你要回傳給前端的物件格式與 Entity 幾乎一模一樣,也不可以直接回傳,要嘛在 Use Case 要轉一下格式,要嘛要套用 CQRS 模式,跳過 Use Case 層,真接與同一層的 Repository 合作,讓它回傳一個客製化的物件。
為什麼這麼麻煩?這時又要請出另一個物件導向設計原則的 SRP:Single Responsibility Principle 了。Entity 身為系統的核心邏輯,理應只關心「領域模型」中最重要的運算與資料,不應該管外層表現的需求。會這樣設計,為的就是使核心業務不被外層的細節干擾。你如果為了一時方便,讓負責與前端網頁溝通的 Adapter 去直接存取 Entity,那以後網頁要拿什麼值,要改什麼格式,核心的 Entity 就不得不改了。
注意到了嗎?「最內層的 Entity 因為最外層的網頁需求,而需要修改」耶!這不是反模式,什麼才是反模式?所以,跨層原則雖麻煩,但好好遵守,架構才會乾淨!
使用 Clean Architecture 以後,元件的安排有了依據,核心運算邏輯不會依賴於細節的資料庫或是網頁,整個系統不論整潔度或是靈活性都比以前好很多。重點是,安排工作時,知道什麼要先做,什麼可以晚點決定,不會進退失據。總之至少目前為止,用起來挺不錯的!
謎之聲:「事有輕重緩急,月有陰晴圓缺 XD」
圖片截自 Wikipedia
ithelp2021