iT邦幫忙

2021 iThome 鐵人賽

DAY 21
3

古語有云:「歲有凶穰;故谷有貴賤;令有緩急;故物有輕重。」旨在告訴後人,做任何事情,一定要先搞清楚狀況,把事情的優先順序排好再開始動手,方為上策。時間有限,但要做的事就是那麼多,「重要的事先做」講起來容易,做起來真的很難。

接下來的幾天,我們會聊一個近年來,後端工程師或多或少都聽過的主題:「Clean Architecture」。

Clean Architecture 出現後,對筆者的工作影響非常巨大。筆者不算是架構很強的工程師,但正因如此,筆者發現在做後端工作時,只要照著 Clean Architecture 的規劃走,就幾乎不會出什麼亂子。而且在結構乾淨了以後,測試寫起來特別好寫,重構起來也比較有個依據。對此,筆者如獲至寶!

因此筆者在本系列文章中,才特別想要來「野人獻曝」一下,不求能教大家太多專門的知識,但期望有多點人能感受到它的好處,而加以研究,使大家工作能更順利,那我目的就達到了。

簡介

Clean Architecture 是 Uncle Bob 在 2017 年出的一本關於「架構」的書。在這之前,出現過很多經典的「系統架構」設計,如 Hexagonal Architecture, Onion Architecture 等。Uncle Bob 認為,這些架構雖各有千秋,但都有一些相似之處:

  1. 與框架分離:框架雖好用,但它就是個工具,把這些工具隔離於你產品的核心邏輯之外,才不會被這些工具限制了你的開發。
  2. 可測試:系統核心邏輯可以獨立於 UI、資料庫、網路等「細節」。這些細節可被任意抽換,以方便我們測試核心邏輯。
  3. 獨立於資料庫:把 Oracle 換掉,換成 MySQL、Mongo、任何其它資料儲存工具,都不會破壞系統本身。事實上你的業務邏輯根本就不知道、也不在乎資料存在哪。

分層

Uncle Bob 認為,前述這些很棒的架構設計之所以很棒,乃因它們都具備上面的特性。於是綜合這些優點,加上自己的見解,Uncle Bob 在 2017 年提出了「Clean Architecture」這個架構。

Clean Architecture 把系統分成四層,這四層不是由上而下排列,而是由外而內排。 排列的依據為:「越細節的越外面,越核心的越裡面。」這四層分別為:

Entities

核心業務、重要資料所在地。不管你在什麼場景,做什麼操作,在你的問題領域中不論有沒有系統幫忙,你都必須得做的事。

Use Cases

這一層包圍在 Entity 之外,定義著對於 Entity 邏輯與資料的使用時機與操作順序,是一個管「自動化」的傢伙。

Interface Adapters

系統的邊界,對外取得請求內容,轉換成內層認識的樣貌、並尋找適當的 Use Case 來執行任務,最後再轉換成外界認識的樣貌送出去。同時,系統對外發出的請求,也會在這一層轉換。

因為位處系統邊界,做的事大多都是一些轉換的工作,所以命名為 Interface Adapter。

Frameworks & Drivers

系統中最細節的部分,大部分都是 I/O,舉凡網路、檔案、資料庫、人機介面等,都屬於此範圍。

那麼什麼叫核心,又什麼叫細節呢?實際操作時,其實就是:「離 I/O 越近的越細節,在外層;離 I/O 越遠的越核心,在內層。」

Uncle Bob 說:「Entity 與 Use Case 是你生意能經營的主要推手,必須慎重對待。I/O 雖然有重要,但是是重要的細節,應該要可以簡單被抽換。」

這裡留個思考題:「Data driven 與 UI driven 的開發方式,與 Clean Architecture 搭配使用的話,會發生什麼事呢?」

三大原則

Clean Architecture 討論了很多設計上的注意事項,而簡言之可以歸納出三大原則:

  1. 分層原則:如上一節所述,所有系統元件應該要分門別類安置,不要亂放。
  2. 依賴原則:依賴應該要盡量「由外而內」,不能「由內而外」,否則依賴關係會亂掉,造成不必要的耦合。
  3. 跨層原則:外層只能認識同一層或往內一層的元件,不能認識「兩層以外」的任何元件。

依賴原則的挑戰

來思考一下。依賴原則說不能由內往外依賴,那當 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 因為最外層的網頁需求,而需要修改」耶!這不是反模式,什麼才是反模式?所以,跨層原則雖麻煩,但好好遵守,架構才會乾淨!

Kuma 的使用心得

使用 Clean Architecture 以後,元件的安排有了依據,核心運算邏輯不會依賴於細節的資料庫或是網頁,整個系統不論整潔度或是靈活性都比以前好很多。重點是,安排工作時,知道什麼要先做,什麼可以晚點決定,不會進退失據。總之至少目前為止,用起來挺不錯的!

謎之聲:「事有輕重緩急,月有陰晴圓缺 XD」


圖片截自 Wikipedia

Reference

  1. Rober C. Martin, Clean Architecture: A Craftsman's Guide to Software Structure, Prentice Hall, 2017
  2. The Clean Architecture:https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
tags: ithelp2021

上一篇
Day 20 「就是真誠」TDD 的實彈演習:Magneto Effect
下一篇
Day 22 「戲如人生」以真實案例分析 Clean Architecture 的分層原則
系列文
你就是都不寫測試才會沒時間:Kuma 的 30 天 Unit Test 手把手教學,從理論到實戰 (Java 篇)30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
Len
iT邦新手 5 級 ‧ 2021-09-21 20:19:58

應該是「越核心的越裏面」吧XD

Kuma iT邦新手 3 級 ‧ 2021-09-21 22:19:20 檢舉

合情合理 XD

我要留言

立即登入留言