以下內容為原創,歡迎轉載,轉載請注明
來自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5276587.html
一個干凈的架構
原文:https://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html

在過去幾年中我們能看到的一系列關於系統架構的思想。它們包括:
-
Hexagonal Architecture(也稱為
Ports and Adapters
),作者是 Alistair Cockburn,並被 Steve Freeman 和 Nat Pryce 在他們很棒的 Growing Object Oriented Software 這本書中采用。 -
Onion Architecture,作者是 Jeffrey Palermo。
-
Screaming Architecture,來自我去年寫的一篇博客。
-
DCI,來自 James Coplien 和 Trygve Reenskaug。
-
BCE,來自 Ivar Jacobson 的一本書 Object Oriented Software Engineering: A Use-Case Driven Approach
盡管這些書在細節上有些不同之處,但是它們是非常相似的。從分離的觀點上來講它們都有着同樣的目標。它們都是通過軟件分層來實現分離的。每一個都至少有一層是用於業務規則,另外一層用於接口。
這些架構生產系統:
-
框架獨立性。架構並不依賴於已經存在的某些庫的有負載的特性。這允許你作為工具去使用框架,而不是把你的系統強塞到限制和約束中。
-
可測試性。業務規則可以脫離UI、數據庫、web服務器和其它外部元素去進行測試。
-
UI的獨立性。UI可以在不修改系統其它地方的情況下很容易地被改變。比如,一個Web UI可以使用console UI替換,而不改變任何業務規則。
-
數據庫獨立。你可以使用Mongo、BitTable、CouchDB或者其它來置換Oracle或SQL Server。你的業務規則不會被數據庫束縛。
-
外部代理的獨立性。事實上,你的業務規則根本不知道外面的世界。
這篇文章頂部的圖表就是嘗試所有這些結構到單個可操作的思想中去。
依賴准則
同心圓表示不同軟件的區域。一般情況下,走得越遠,軟件水平也會變得越高。外面的圓是機制,而內部的圓是政策。
使得這個架構可行的最重要的規則是 依賴准則 。這個准則指 代碼的依賴 只能 向內 。沒有一個內部圓可以知道外部圓的任何東西。尤其是在內部圓聲明的東西名字不能被內部圓中的代碼提到。這里包括方法、類、變量、或者其它軟件實體的名字。
同樣的原因,外部圓中用到的數據格式不能被內部圓使用,尤其是這些格式是被外部圓中的框架生成的。我們不希望任何在外部圓中的東西影響到內部圓。
Entities
實體封裝了 企業級 業務規則。一個實體可以是一個有方法的對象,或者有一系列的數據結構和函數。只要這個實體可以被很多不同企業中的應用使用就沒關系。
如果你沒有一個企業,並且只是編寫單個的應用程序,那么這些實體就是應用程序中的業務對象。它封裝了大部分一般的和高級別的規則。它們最不可能在外部改變的時候被改變。比如,由於安全你不希望這些對象被導航頁面改變而影響。沒有特別的可操作的改變可以影響實體層。
用例
軟件在這一層包含應用程序指定的業務規則。它封裝和實現了這個系統的所有用例。這些用例轉化數據流到實體和從實體轉化到數據流,然后這些實體直接使用它們企業范圍的業務規則來達到用例的目的。
我們不期望改變這一層來影響到實體。我們也不希望這一層被外部的改變,比如數據庫、UI、任何常見的框架等影響。在這一層在這的觀點上是被孤立的。
然而,我們要做的是希望應用程序操作的改變會影響到軟件的用例,所以應該是在這一層。如果用例的細節改變了,那么這一層的某些代碼當然也會受到影響。
接口適配器
在軟件的這一層是一系列的適配器,通過最方便的用例和實體轉換數據格式,最方便的外部代理格式有數據庫或者Web等。在這一層,舉個例子,將會完整包含 GUI 的 MVC 架構。Presenters、Views、 Controllers 都屬於這里。Models有可能僅僅是數據結構,它們從 contrllers 被傳入到用例中,然后從用例到 presenters 和 views。
同樣的,在這一層,數據會被轉換,從最方便的方式如實體和用例,轉換到使用的用於持久框架的最方便的方式,即數據庫。
這個圓中沒有代碼可以向內知道任何關於數據庫的東西。如果數據庫是一個 SQL database,那么所有 SQL 應該在這一層被限制,特別是在這一層需要進行數據庫操作的部分。
在這一層也有必要從一些外部形式去轉換數據,比如一個外部服務,轉化為內部用例和實體使用的形式。
框架和驅動
最外層通常是框架和工具,通常是數據庫、Web 框架等的組合。通常你不需要在這一層編寫太多的代碼,除了一些用於向內圈進行通信的固定代碼。
這一層是所有細節走向的地方。Web是一個細節。數據庫是一個細節。我們把這些東西放置在外部,這樣它們就難以造成傷害。
只有四個圓?
不,這些圓只是簡圖。你可能會需要多於這四個圓。這里並沒有一個規則來讓你必須要使用這四個圓。然而,依賴規則 總是適用的。代碼依賴總是指向內部。當你向內移動時,抽象級別增加。最外層的圓是最低級別的具體細節。當你向內移動,就會變得更加抽象和更高級別策略的封裝。最內部的圓是最通用的。
跨越邊界
右下方的圖是一個我們怎么去跨越圓的邊界的例子。它展示了 Controllers 和 Presenters 通過下一層使用用例進行通信。注意控制流。它在controller中開始,通過用例,然后再Presenter中執行。也要注意代碼的依賴。它們每一個都是向內指向用例。
我們通常使用 依賴反轉准則 來解決這個明顯的矛盾。在像Java的語言中,舉個例子,我們使用了interfaces和繼承關系,這樣代碼依賴關系控制權被反轉,達到跨越邊界的目的。
舉個例子,考慮到用例需要調用Presenter。然而,這些調用必須不能是直接的,因為它會違反_依賴准則_:內部圓不能提到外部圓中的任何名字。所以我們這種情況我們會調用在內部圓中的接口(就像這里展示的 Use Case Output Port)),然后在外部圓中的Presenter去實現它。
同樣的技術在架構的所有跨越邊界的地方被使用到。我們利用動態代理的優勢去創建代碼依賴來達到控制反轉,所以我們可以確保無論什么方向的 依賴准則 控制流都能有效。
什么數據跨越邊界。
典型的跨越邊界的數據就是簡單的數據結構。如果你喜歡你可以使用基本結構或者簡單的數據傳輸對象。或者是方法調用時的簡單的參數數據。或者你可以把它放進一個HashMap,或者構建它到一個對象中。重要的是分離的、簡單的數據結構跨越過邊界。我們不希望去欺騙並傳遞 Entities 或者數據庫的行。我們不希望數據結構有任何的違反 依賴准則 的依賴。
舉個例子,很多數據庫框架通過一個查詢返回一個方便的數據形式的響應。我們可能稱它為一個RowStructure。我們不希望這個行結構向內跨越邊界。這將違反 依賴准則 因為這將強制一個內部圓去知道外部圓的一些東西。
所以當我們跨越邊界傳遞數據時,它對於內圓來說總是以最方便的形式的。
遵守這些簡單的規則並不難,前進的道路上會解決很多頭疼的事情。通過分離軟件到不同層次,並遵守 依賴准則 ,你將會創建一個本質上可測試的系統,這意味這所有的好處。當任何系統外置的部分變得過時,就像數據庫,或者web框架,你可以在最小的改動下替換這些外置元素。