歡迎光臨
每天分享高質量文章

從 Clean-Architecture 談架構原理及其應

前些日子和團隊的小伙伴們分享了自己對架構的理解,當時準備的比較倉促,講的也比較粗糙,很多點並沒有表達清楚;欣慰的是大家的反饋都比較積極,證明分享的內容是有參考價值的。
這篇博客主要把分享的內容進行整理重塑,並補充一些當時沒有表達的細節,期望能夠給更多人更多的啟發。
團隊小王的疑惑
聖誕那天晚上我值班,時間已經很晚了,發現團隊小王同學還沒回,好奇心驅使於是走過去聊了幾句:原來他等著與另一個系統同步發版,辛苦卻無恙。不過我好為人師的臭毛病(主要對新同學)發作,於是各種問東問西。作為過來人我太清楚職場新人的處境了,疑惑多,如果性格偏內向還不喜歡表達困惑。瞭解後發現小王果然存在疑惑——如何通過架構設計降低業務代碼修改的難度,並拋給我一篇博文鏈接(https://manuel.kiessling.net/2012/09/28/applying-the-clean-architecture-to-go-applications/) ,同時聲稱鏈接是我們組另一個技術大佬推薦的。
在打發了小王同學後,抱著“不知就要學”的心態,我打開了他給我的那個鏈接,主要講的是用 Go 語言實現一種架構,內容可謂又臭又長。當天夜裡無眠,我一邊盯著監控面板一邊理解那篇博客的內容。到第二天調班休息的時候,心裡還放不下那篇博客,於是困了睡,睡醒了就打開電腦讀幾段,感覺到困了再繼續睡,最後終於理解了文章的主旨思想及其實踐的含義。
這件事情觸發了我幾點思考:1)小王同學的疑惑是否值得探索並解答,即“如何通過架構設計降低業務代碼修改的難度”;2)能夠通過架構設計降低業務代碼修改(對應功能的增刪改)的難度嗎?3)小王做的專案的架構最初是我參與設計的,如果讓我對架構進行二次設計,我是否會採用其他的架構方案?4)這個專案使用了團隊內部的研發模板進行初始化,而模板的原型我也是參與制定者之一,那麼這個模板所包含的架構設計是最佳實踐嗎?5)如果讓我二次設計研發模板,會改成另一種設計嗎?
通過思考得到上面問題的答案後,結合經驗我又進一步深化琢磨了幾個問題:a)團隊應該採取什麼樣的方式來減少職場新人的困惑,並盡可能降低這種困惑給團隊和當事人引起的不適感?b)職場新人應該如何提升自己從而擁有架構的能力?c)團隊里的知識應該採取什麼樣的方式進行傳遞繼承?d)對於一些高階的方法論,如何讓實踐經驗欠缺的同事吸收消化並運用到實際工作中?
問題需要一個一個地解決。

 

Clean-Architectrue(整潔架構)
過去對架構思考的比較少,不過最近兩個月因為工作上遇到的一些問題我一直在琢磨“分層”的技巧,並嘗試論證“分層”的好處,同時細化“分層”所應遵循的原則,標的是提升自己的效率,優化團隊成員的合作效率。順著小王同學給的鏈接瞭解到整潔架構(The Clean Architectrue)的理論,算是找到了“分層”的理論支持,真可謂一個意外的收穫了,
Clean-Architectrue的整體思想
Clean-Architectrue架構圖,摘自The Clean Architecture
分層思想
上圖顯示了整潔架構的示意圖,其中每個同心圓層代表了軟體開發中的不同層,越是靠近中心,同心圓層所代表的東西越抽象。可以這麼理解,內部的圓層定義的是規則,外部的圓層定義的是實現機制。
整潔架構試圖聚合這樣幾個特點:1)框架無關,即架構設計不依賴任何一個既有的開發框架,因此理論上可以使用任何框架來實踐整潔架構。2)業務規則的可測性,即可以方便地測試業務邏輯所涉及的代碼,不依賴UI、資料庫或者 Web 服務等外部元素。3)功能實現不依賴 UI 的實現細節,比如同樣一套後臺系統可以用在 Web 應用,也可以用在 App 原生應用。4)業務邏輯不依賴資料庫的實現細節,可以把資料儲存在 Oracle、SQL Server、Mongo、MySQL 等任意一種資料庫中,同時業務邏輯不需要做任何改變。5)總結起來就是:業務邏輯對外界實現完全沒有依賴,任你外面的實現細節如何改變,核心業務邏輯不需修改。
依賴規則
為了實現上面所講的特點,只需要遵循依賴規則:只允許外部圓層的代碼依賴內部圓層的代碼,反之則禁止。換言之,內部同心圓層的代碼不知道任何外部同心圓層的代碼,比如內層的代碼一律禁止取用外層宣告的函式、類、變數或其他任何元素。
其實上面的規則還隱含著另一個規則:當有資料傳遞時,只允許外部圓層接受內部圓層的資料格式,反之則不允許。這也是為避免外部的代碼邏輯影響內部的代碼邏輯(思考一下為什麼)。
整潔架構中的術語介紹
  • 物體(Entities)層:主要封裝企業範圍的業務規則,可以認為是代碼要實現的核心業務規則,比如電商里涉及到的購物規則。

  • 用例(Use Cases)層:主要封裝特定於應用的業務規則,比如電商里普通用戶購物與管理員管理貨物信息,二者所涉及的用例是不一樣的。

  • 接口適配(Interface Adapters)層:主要包含各種配接器,負責把從物體層和用例層流出來的資料轉換成為更外面一層需要的資料格式(比如轉換成為適合資料庫儲存的格式,或者適合 Web 頁面展示的格式)。

  • 框架與驅動(Frameworks and Drivers)層:這是最外面的一層,是很多工具(或庫)的具體實現細節所在層,比如 Web 框架(考慮一個應用可以做成網頁版應用,也可以做成單機版應用)、資料庫工具包(考慮各種 ORM 的實現,以及各種資料庫的驅動依賴包實現)等。

值得說明的是,雖然整潔架構的示意圖中只顯示了四層結構,但是可以根據業務規模調節層數,也就是說層數不是關鍵,關鍵在於分層思想以及依賴規則。
一個關鍵點——控制反轉
依賴規則里規定“只能外層的代碼依賴內層代碼”,但是在實際代碼中勢必會出現這種場景:外層的代碼呼叫內層的代碼處理資料,資料經過內層代碼作用後需要再反傳給外層進行其他處理(比如把處理好的資料儲存到資料庫,儲存到資料庫的邏輯在外層)。Clean-Architectrue架構圖的右下角給出了一種示意,那麼如何讓這種場景滿足依賴規則呢?答案就是控制反轉。
對於 Go 語言來說,大體的實現是在內層定義一個 Interface 型別的資料,內層代碼通過操作 Interface 的方法來實現業務邏輯,在外層實現這個 Interface 型別所包含的方法,從而達到反向控制的目的。其實這是一種設計樣式,有點類似工廠方法樣式或者抽象工廠樣式,不是 Go 的專屬,其他語言(比如 Java)均可以實現。
知己知彼以後,我設計的架構有問題嗎
在領悟了整潔架構的思想後,我又把小王同學給我的又臭又長的博客讀了兩遍,然後思考我做的專案架構以及參與制定的開發模板是否有必要修改,假如要修改的話如何修改?如果不需要修改的話理由是什麼?
經過一番分析後,我認為我做的那個專案架構沒有問題(不排除一些細節存在疏漏),不需要修改。我是從具體的業務複雜度來考慮的,改進版的 MVC 架構已經足以應對。1)假如按照整潔架構的思路把專案重構一遍,並沒有辦法降低需求的增刪改造成的開發量,該頭疼的地方依然要頭疼;2)專案採用小巧精悍的 MVC 架構也能夠滿足快速敏捷的要求,而整潔架構的定位是大型的企業級應用或應用生態,反而不適用於小複雜度的專案開發;3)重構是一件勞民傷財的事情,輕易不能做。這麼分析以後,我參與自研的開發模板已經足夠大部分專案使用,至少架構層沒有修改的必要(其他的層面或許有改進的必要,比如通用依賴庫的使用方法、測試用例編寫規範等)。
雖然思考到最後發現不需要做什麼,但這次思考的意義在於,讓我意識到架構這件事情,並開始思考架構的含義及意義。那麼,架構是什麼呢?

 

架構是什麼
架構是什麼呢,可能這個問題和“什麼是最好的編程語言”一樣,不同的架構師會有不同的觀點。在 2018 年上半年的時候,我參與過一次雷達峰會,對其中一位講師丟擲來的觀點比較認同(當時沒有太大感觸,思考完上面的問題後才開始反芻這些觀點):1)架構為業務而生;2)架構聚焦於解耦;3)架構意味著溝通與協作;4)架構需要為時間買單(可以理解為架構的進化)。
我們可以先看幾個流行的架構圖來體會一下。
幾個流行的架構圖
Kubernetes 的架構圖:
相信每個瞭解雲技術棧的開發者都見過上面的 Kubernetes 架構圖,至少聽過其中部分組件的名稱。這張圖很清楚地描述了 Kubernetes 包含的各個組件及其協同運作的方式,這不僅僅給 Kubernetes 專案的開發者一些開發方向的指引,同時也讓 Kubernetes 的使用者能夠方便地討論各種問題。
Istio 架構圖:
業界提到 Service Mesh 的時候必提 Istio,它的架構如上圖所示。可以註意到在 Istio 的架構中,所有的流量都通過 Proxy 進行接管,而 Proxy 是作為一個獨立的行程與業務行程運行在同一個實體中(多指 Kubernetes 里的 Pod),那麼對這個 Sidecar 就有比較多的要求了,比如要輕量級、高效、便於配置部署等等。由架構圖我們也可以反推得出,在微服務化改造的過程中,應該控制自己業務的複雜度不能拆的太細碎,否則邊界成本會變得非常大。
VXLAN虛擬網絡架構圖:
這是我之前的一篇博客的示例圖,描述了 VXLAN 的虛擬網絡架構,這張圖讓很多討論變得十分容易。
架構存在的意義
  1. 就像前面描述的,架構存在的主要意義是為瞭解決某個業務問題,最終都可以對應到某個具體的技術方案;換句話說,世上沒有通用的架構方案,只有適合某個業務難題或某個領域難題的架構方案。面對架構的問題,我們不能生搬硬套,如果想得到適用的架構,必須要深入瞭解業務的細節,唯一的捷徑是承認這個事實,沒有其他捷徑。

  2. 在把握了業務的所有細節後,接下來做的一件事情是解耦,把業務細節分類分組,拆分成不同的模塊,繼而放到不同的層里。

  3. 定義好了模塊的邊界和不同層的邊界,接下來就可以派發工作了。有了詳細完整的架構設計,大家協同開發也變得簡單順利;首先大家溝通的時候能夠十分清楚彼此要表達的問題細節,其次各個模塊開發完到功能對接的時候彼此也能快速理清楚關註的點(比如必須要實現哪些個方法、函式或接口)。

  4. 最後在大家的共同努力下,專案終於完成了,但是不要以為到此結束。根據實踐經驗來看,所有的專案都會有新需求,或需求更改,這個時候很大概率會對原有的架構造成衝擊。架構上該如何應對呢?理論上只要業務沒有徹底改變,在原有的架構上做調整就可以滿足需求了。因此原有架構的資料(所有圖文文件、音視頻等)就變得很重要,甚至一些決策的細節參考標準有時候也能起到關鍵的作用(寫文件是給架構方案添磚加瓦的一種方式)。

架構師養成方法
架構設計,往往要求開發者擁有宏觀的視角,在實踐經驗不足、業務瞭解不夠全面的情況下,是談不上做架構的。不過,大部分開發者雖然做不了大的架構,但是可以嘗試做一些微小的架構設計,從而建立自己的信心,然後 1)努力刻苦鍛煉自己的實戰經驗(下苦功夫瞭解所在技術棧里的框架細節、依賴庫細節、中間件細節,等等),2)同時積極主動地瞭解、思考盡可能多的業務細節。如此堅持下去,成為牛 x 哄哄的架構師指日可待。
領域驅動設計(DDD)
領域驅動設計(DDD)是一種實用的架構實踐方式,它是一個成體系的方法論,這本書 詳細地介紹了這個實踐。文本無法通過一個小節的文字詳述它,僅通過它的兩個前提和兩個實踐方法先大體瞭解一下它的概念。
DDD 的兩個前提:1)在⼤大多數軟體項⽬目中,主要的焦點應該是領域和領域邏輯; 2)複雜的領域設計應該基於模型。這兩個前提條件把領域驅動所關註的點變成了模型的創建,因此這本書 很大篇幅的內容都是在講模型相關的內容。
DDD 的兩個實踐:1)迭代開發;2)開發⼈人員與領域專傢具有密切的關係 。第一條側重技術上的持續性,第二條側重業務上的理解。

 

小結
個人小結
從小就認定了“好好學習”的道理,所以一直非常努力刻苦地對待學業,不過那時候除了有一個好成績外並未感覺到對自己的影響,甚至在碩士學位結業的時候我還在懷疑自己刻苦那麼多年的意義。參加了工作後,一切與錢掛鉤,那種努力卻看不見成長的感覺挺折磨人的,這一度讓我感到迷茫,並且持續了好久好久。終於有一天,就好像從量變到了質變(我印象中是讀瑞·達利歐的《原則》這本書那段時期,那段時期發生比較多的事情),一切都漸漸變得清晰明朗。我發現很多工作中需要的知識已經在課堂上學習過,生活中的很多道理也已經在課堂上學習過。我知道了自己所知道的,從而加以運用;同時也意識到自己所不知道的,從而有的放矢地學習精進自己的知識儲備。
本文小結
本文簡單介紹了 Clean-Architecture 的思想,並通過幾個示例介紹了架構原理及其應用實踐,最後嘗試著解答了職場新人對架構的一些疑問,並給出了一些精進才能的建議。最後小結部分感慨了一下自己的職業生涯,給自己的未來定了一個基調——向著另一個更高的未知層衝刺。
原文鏈接:https://jingwei.link/2018/12/31/thinking-of-architecture.html
赞(0)

分享創造快樂