洋蔥架構簡介——分離是為了更好的結合


寫出高質量軟件是困難和復雜的:不僅僅是為了滿足需求,還應該是健壯的,可維護的,可測試的,並且足夠靈活以適應成長和變化。這就是洋蔥架構出現的原因,它代表一組優秀的開發實踐,用來開發任何的軟件應用都是一個不錯的方式。

洋蔥架構,也稱為整潔架構(The Clean Architecture),用來構建具有如下特點的系統:

1.    獨立的Frameworks
2.    可測試
3.    獨立的UI
4.    獨立的數據庫
5.    獨立的任意外部服務(代理)

看到這張圖,你應該能理解為什么稱其為洋蔥架構了:D, 沒錯,這就是它的原理圖。注意,並不是只能使用4個圓環,重點在於這里的依賴原則:代碼依賴是從外向內的,內環中的代碼不應該知道外環中的任何東西。

這里有一些相關的詞匯可以幫助更好的理解和熟悉這種方式:

  Entities:應用的業務對象。

  Use Casess: Use Casess協調(Orchestrate)數據從Entities的流入和流出,也被稱為Interactors。

  Interface Adapters:這個Adapter集為Use Casess和Entities把數據轉換為方便使用的格式(如渲染展示在頁面上),Presenters和Controllers屬於這里。

  Frameworks and Drivers:這是實現所有細節的地方:UI,Tools,Frameworks等。

 

下面用一張更生動圖來輔助說明它的原理:

依賴原則(The dependency rules)

上面的同心圓代表軟件的不同部分。總得來說,越往里面,代碼級別越高。外層的圓是(實現)機制,而內層的圓是原則(Policies)。

讓這個架構起作用的最主要原則是依賴原則。這個原則要求源碼依賴只能指向內部。內部的圓不能知道外圓的任何事情。一般來說,外圓的聲明(包括方法、類、變量或任何軟件實體)不能被內圓引用。

同樣的,外圓使用的數據格式不能被內圓使用,尤其是外圓中的Framework產生的格式。我們不想讓外圓的任何東西影響內圓。

 

越往里面抽象級別越高,最外層的圓是低級別的具體細節。越往里面內容越抽象,並且封裝更高級別的原則(Policies)。最里面的圓是最通用的。

Entities

Entities封裝了企業級的業務規則。一個Entity可以是一個帶方法的對象,也可以是一個數據結構和方法集。Entities可以被用於企業的其他應用。

如果你沒有加入企業,而是僅僅在寫一個簡單的應用,那么這些Entities就是這個應用的業務對象。它們封裝了最通用、最上層的原則。它們是最不容易改變的,即使外部的東西改變了。例如,你不想讓這些對象受到頁面導航、安全的影響。應用的任何操作變化都不應該影響Entities Layer。

Use Casess

這一層包含了應用特有的業務規則。它封裝和實現了系統的所有用例。這些用例協調數據從entities的流入和流出,並且指導entities使用它們的企業級業務規則來達到用例的目標。

我們不希望這一層的改變影響到Entities,同時也不希望這一層被外層的改變影響,如外層的數據庫,UI或者任何Frameworks的改變,這一層獨立於這些關注點。

當然,我們確實期望應用的操作變化影響用例層。如果一個用例的細節改變,那么這一層的部分代碼確實會受到影響。

Interface Adapters

這一層包含一個adapters set(數據適配器集),它們把適用於Use Casess和entities的數據轉換為適用於外部服務(external agency,如Database或Web)的格式。 例如,這一層可以完全包括GUI的MVX架構,Presenters, Views和Controllers都屬於這里。Models可能僅僅是從Controllers傳到Use Casess的數據結構,然后從Use Casess返回給Presenters和Views。

 

這一層的數據會被轉換,從適用於entities和Use Casess的格式轉換到適用於所使用的持久化框架的格式(如數據庫)。這個圓以內的代碼不應該知道關於數據庫的任何東西。如果是一個SQL數據庫,那么所有的SQL應該被限制到這一層,並且通常來說是被限制到層中跟數據庫有關的部分。

 

同樣,這一層也需要一些其他必要的Adapter來把外部的數據格式(如來自於外部服務的格式),轉換為適用於Use Casess和entities的格式。

 

Frameworks and Drivers.

最外面的一層通常由Frameworks和Tools組成,如Database,Web Framework等。一般來說,除了用於和內層圓交互的連接代碼,你不會在這一層寫很多代碼。

 

這一層是實現所有細節的地方。Web和Database都是需要實現的細節。我們把這些東西放在外面以減輕來自於它們的傷害(即減輕對他們的依賴)。

 

跨界

 

在圖的右下角是一個我們應該如何跨界的例子。它展示了Controllers、Presenters與下一層的Use Casess的交互。注意控制的流向,它開始於Controller,經過Use Casess,最終在Presenter中執行。同時也請注意Source Code依賴,它們每一個都指向內部的Use Casess。

 

我們通常用依賴倒置原則來解決這個明顯的矛盾。比如,在Java這樣的語言里,我們可以使用接口和繼承關系在合適的地方讓源碼依賴與控制流反向來跨界。

 

例如,假設Use Cases需要訪問Presenter,當然,不能是直接訪問,不然會違反依賴原則,所以我們讓內圓的Use Cases訪問一個接口(如圖中的Use Cases output port),然后外圓的Presenter實現這個接口。

在這個架構中,同樣的技術也被用於跨越其他的邊界。我們利用運行時多態來創建與控制流相反的SourceCode依賴以滿足依賴原則,無論控制流是如何流向的。

 

通常跨界的數據都是簡單的數據結構。你可以使用簡單的結構或數據傳輸對象(Data Transfer Object)。這個數據可以簡單的是方法調用的參數,你也可以把它包裝到一個HashMap或者一個對象。最重要的是獨立的、簡單的數據結構才能跨越邊界。不要投機取巧,如傳輸Entites或者Database rows。我們不想讓這個數據結構有任何違反依賴原則的依賴。

 

例如,很多的數據庫框架對於query返回一個方便的數據格式,我們可以稱之為Row Structure,我們不想向內部傳遞這個row structure。這會讓內圓知道外圓的內容而違反了依賴原則。

 

所以,我們應該以最適用於內圓使用的格式來傳遞跨界的數據。

 

總結

滿足這些簡單的原則並不難,並且會減少項目進程中很多頭疼的問題。通過把軟件分成幾層,並且滿足依賴原則,你將會創建一個本身就可測試的系統,同時還有其他的好處。當系統的任何外層部分(如Database,Web 框架)廢棄的時候,你可以輕松的替換這些廢棄的元素。

代碼實踐可以參考:https://github.com/android10/Android-CleanArchitecture/releases

本文大部分內容譯自The-Clean-Architecture,其中加入了自己學習該架構時的理解,如有意見和建議,歡迎交流!

 

參考:

The-Clean-Architecture:http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html

 

Architecting-Android-The-Clean-Way:http://fernandocejas.com/2014/09/03/architecting-android-the-clean-way/

Architecting Android…The evolution:http://fernandocejas.com/2015/07/18/architecting-android-the-evolution/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM