天天看點

一文讀懂架構整潔之道原則分層與解耦架構六邊形架構總結

相信大家都非常清楚,如何編寫可讀性強的代碼是一個合格程式員的必修課。

我在之前的文章《談談什麼是好的代碼》中談了一些自己對整潔代碼的感悟,代碼并不是獨立存在的,成百上千個類的系統在企業應用中非常常見,如何将代碼進行有效的組織,保持高可讀性,高可維護性,則是一個好的架構需要考慮的事情。本文從原則切入,聊聊元件的分層和解耦,淺談下Bob大叔提出的整潔架構,感興趣的同學也可以發表下自己的看法。

原則

原則屬于做事情的指導方針,在讨論架構前,先來看看相關的一些原則。有适用于代碼層面的原則,有适用于再高一層級——元件的原則。

▐ 代碼原則

代碼的原則有SOLID,迪米特法則,組合複用原則等等,我在談談什麼是好的代碼中也列出來過,關于這些原則讨論的文章非常之多,大家也比較熟悉,本文主要談架構方面的,這裡就不展開了。

▐ 元件原則

元件是一組代碼的集合,拿蓋房子來打比方,代碼原則指導如何使用磚塊建造房間,而元件原則指導如何将房間建構成高樓大廈。元件的建構要遵循一些原則,否則即使牆砌的再好,房間修建的再漂亮,不按照規範建造,房子可能就歪歪扭扭,整棟樓房的品質也堪憂。

元件原則包括元件内的關系(元件聚合)以及元件間的關系(元件耦合)。

元件聚合

元件聚合方面的原則有以下幾個:

  • REP:複用/釋出等同原則
  • CCP:共同閉包原則
  • CRP:共同複用原則

REP是元件聚合總的指導原則,表示元件内的類和子產品是彼此緊密相關的。CCP和CRP是對REP的補充,CCP可以看做是元件級别的單一職責原則(SRP):由于相同原因修改,并且需要同時修改的東西放一起;不同原因修改,并且不同時修改的東西分開。CRP可以看做是元件級别的接口隔離原則(ISP):不要依賴不需要的東西。

可以看出通過這三個原則建構的元件,擁有以下幾個特點:

  • 元件内的類和子產品緊密相關,需求變更時通常需要同時修改;
  • 需求變更時,需要進行的修改隻涉及很少的元件甚至在一個元件内;
  • 複用這個元件時,通常元件内的功能均是使用者需要的,而不是有一些不相關的功能;

這裡貼下書中的張力圖:

一文讀懂架構整潔之道原則分層與解耦架構六邊形架構總結

項目的初期,更多關注的是維護性而犧牲複用性,随着項目逐漸成熟,項目重心會逐漸傾向于複用性。

元件耦合

元件耦合方面的原則有以下幾個:

  • ADP:無依賴環原則
  • SDP:穩定依賴原則
  • SAP:穩定抽象原則

元件間的依賴如果存在環,則維護性将大大降低,我們應該避免元件間的循環依賴。

元件間的依賴關系應該是指向更穩定的方向,每個元件的穩定性都低于其依賴的元件穩定性。元件越穩定,則其抽象程度需要更高;元件越不穩定,則其抽象程度需要更低,越具體。

有時候,可能會新引入一個引用,導緻一個穩定的元件依賴了一個不穩定的元件,此時就可以使用依賴倒置原則(DIP)将依賴關系反轉,保持穩定依賴。

分層與解耦

談了元件相關的原則,現在來談談元件間的分層和隔離的方式。通過有效的分層手段,可以有效隔離不同功能的元件。

▐ 水準分層

得益于MVC模式的普及,水準分層在我們的系統中已經非常普及了,通常有以下幾層:

表現層/UI層:負責系統的界面展示,通常包括Controller和VO等;

領域層/業務邏輯層:負責處理系統的業務邏輯,通常包括Service,Manager和DTO等;

資料層:負責與DB等底層資料存儲媒體通信,通常包括Mapper和DO等;

▐ 垂直分層

但是,僅僅做到水準分層是不夠的。當業務邏輯比較複雜時,涉及的用例會非常多,這些用例之間的變更原因幾乎肯定是不同的,是以還要進行垂直分層,将變更原因不一樣的用例切分開。

例如,很多系統的修改操作和删除操作的變更原因和變更頻率是不一樣的,這時候可以考慮将修改和删除進行解耦。

不論是水準分層還是垂直分層,其核心目的都是将更新頻率不同的代碼給分開,放入不同的元件中。

▐ 解耦方式

分層後的解耦方式有多種:

  • 源碼層次:通過控制源代碼子產品間的依賴關系進行解耦,部署時仍然一起部署,适用于項目早期剛起步時
  • 部署層次:通過控制部署單元(例如jar包等)之間依賴進行解耦,當系統對部署和開發方面有更高的要求時,部分元件需要獨立出去形成新的部署單元;
  • 服務層次:通過将元件的依賴關系降低到資料結構級别,然後通過服務進行通信來解耦,當系統足夠大的時候,就需要服務層次的解耦了;

但需要注意的是,不論通過哪種解耦方式進行代碼的隔離,并不意味着這樣就萬事大吉,擁有良好可擴充和可維護性了。這也是Bob在《架構整潔之道》中提到的“橫跨型變更”(雖然作者是在服務的這一章提出的,但我認為也适用于其他兩種解耦層次)。

請看下面的計程車排程系統的服務架構圖

一文讀懂架構整潔之道原則分層與解耦架構六邊形架構總結

圖中可以看出,Taxi UI依賴 Taxi Finder查找符合條件的計程車,依賴Taxi Selector進行計程車排程。而Taxi Finder通過多個Taxi Supplier服務擷取車輛資訊,Taxi Selector依賴Taxi Dispatcher進行最終的派單。

各個元件都是服務化的。可以看到,各個元件都是具體的類,雖然各個元件隔離部署,但其實他們之間是強耦合的,并沒有真正的解耦:加入現在計程車公司準備推出運送貓咪的服務,則所有的元件都需要進行更改,同時有些同學的更改方式就是在原有的類中增加if...else,這顯然是不可取的。

正确的做法應該是在元件最初的設計中,就應該考慮抽象化和多态,如下圖,使用政策模式或者模闆方法進行解耦:

一文讀懂架構整潔之道原則分層與解耦架構六邊形架構總結

采用面向對象的方法來處理橫跨型變更

注意看我紅框标出來的,除了服務間的隔離外,在元件内部其實也存在隔離,而這個的隔離更加重要。這也就是書中講的:

服務邊界并不能代表系統的架構邊界,服務内部的元件邊界才是。

架構

從代碼群組件原則到元件分層和解耦,我們逐漸對系統底層的一些元素有了比較深入的了解,那麼上層的架構到底是什麼呢?

▐ 什麼是架構

并沒有非常明确的定義,這裡引用書中的一些描述,大家應該有一些體會:

軟體架構的實質就是規劃如何将系統切分成元件,并安排好元件之間的排列關系,以及元件之間互相通信的方式。

軟體架構的終極目标是,用最小的人力成本滿足建構和維護該系統的需求

需要指出的是,架構和架構并不是相同的東西:

  • 架構一定是業務相關的,包含了業務屬性,并且這個業務屬性是系統的核心價值;
  • 架構一般都是業務無關的,是我們編碼實作架構的的工具,屬于實作細節。

最初設計系統架構時,并不需要過多考慮使用什麼架構,而更多的是關注自身業務。

此外,很多人可能對架構有些誤解:設計那麼多有什麼用,代碼不還照樣得寫?

是的,代碼還得寫,架構并不能讓你不寫代碼了(有時可能還會讓你多寫代碼)。但是,好的架構會讓寫代碼變得更容易了。

容易不一定是展現在需要變更的代碼量多少上,好的架構可以讓你更快速的找出需要變更的範圍,并且很容易的就修改掉——這對于一個運作維護了多年的系統尤為重要。大家可以回想下這樣的場景是不是很熟悉:明明是一個看起來很正常很合理的需求,看起來變更範圍也不大,但真的去撸代碼時發現需要改的地方好多,甚至無從下手去改。這個時候可能就要去看看之前的架構設計是不是不夠合理,有哪些需要優化改進的。

六邊形架構

六邊形架構,又名端口擴充卡架構(我更喜歡這個名字,因為六邊形老讓人感覺有六個什麼東西跟它對應),DDD極力推崇該架構。系統的領域模型是系統最為重要的部分,而其他的諸如DB,UI,緩存,消息隊列等等均通過擴充卡與領域層進行通信,也就是依賴關系是由外到内的(依賴倒置)。之前在商家規模化營運項目中也嘗試過使用DDD來進行架構設計。

一文讀懂架構整潔之道原則分層與解耦架構六邊形架構總結

▐ 整潔架構

Bob綜合六邊形架構和其他幾個架構的特點提出了整潔架構,它具有以下幾個特點:

  • 獨立于架構
  • 可被測試
  • 獨立于UI
  • 獨立于資料庫
  • 獨立于任何外部機構
一文讀懂架構整潔之道原則分層與解耦架構六邊形架構總結

架構最内部是業務實體,代表了系統關鍵業務邏輯。

再外一層是用例——特定應用場景下的業務邏輯,在Bob看來,用例是架構設計中非常重要的一環,架構也是基于這些用例進行設計的,如果缺失了用例,就無從談起架構設計了。

緊接着是控制器,網關和展示器,是一層接口擴充卡。

最外層則是架構和驅動程式,這裡面包含了資料庫,web,工具等等,這一層一般隻包含了一些通信的黏合性代碼。

也并不一定隻有四層,真實的情況可能會超過四層,但總的原則不變:外層依賴記憶體,最内部是最通用、最高層的政策,最外層是最具體的實作細節。

外層的是底層元件,内層的是高層元件,底層元件作為高層的插件存在,換句話說外層的這個元件是可以被其他的元件像插件一樣替換掉的。

這裡需要明确的一點是:有時候,軟體運作的方向與依賴的方向是不同的(這個很多同學可能會沒有注意到)。比如業務實體從資料庫取資料,運作方向是業務實體->資料庫,而通過依賴反轉(DI,業務實體定義查詢接口,資料庫層實作),我們的依賴關系是業務實體<-資料庫。這裡講到的保持底層元件對高層元件的依賴,高層元件的穩定性和抽象性,也就是前面講的SDP和SAP。

總結

本文從代碼群組件的原則講起,講到元件内群組件間的關系,以及如何進行元件的分層和隔離,接着引出了架構相關的讨論,列舉了六邊形架構和整潔架構,并談了一些自己的了解。

後續會結合自身做過的項目,談一談具體的一些架構模式。

參考:

《架構整潔之道》 Robert C. Martin

《領域驅動設計》 Eric Evans

《企業應用架構模式》Martin Fowler