天天看點

【譯】The Clean Architecture依賴規則實體用例接口擴充卡架構與驅動隻有四個圈?跨越邊界什麼樣的資料會穿越邊界結論

Robert C. Martin (Uncle Bob)

原文:

https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html 譯:時序
【譯】The Clean Architecture依賴規則實體用例接口擴充卡架構與驅動隻有四個圈?跨越邊界什麼樣的資料會穿越邊界結論
在過去幾年我們看到關于系統架構的很多想法。這些包括:

盡管這些架構在一些細節上都有不同,它們仍是相似的。他們都有同樣的目标,隔離關注點。他們都通過将軟體分層來達到隔離。每個都至少有一層業務規則,另一層作為接口。

每個這些架構産出的系統都是:

  1. 獨立的架構。架構不依賴一些存在類庫的特性。這樣你可以像工具一樣使用這種架構,而不需要讓你的系統受到它的限制條件。
  2. 可測試。業務規則可以脫離UI,資料庫,web伺服器或其他外部元素進行測試。
  3. 獨立的UI。UI可以很容易的更換,系統的其他部分不需要變更。例如,Web UI可以被換成控制台UI,不需要變更業務規則。
  4. 獨立的資料庫。你可以交換Oracle或SQL Server,用于Mongo,BigTable,CouchDB或其他的東西。你的業務規則不與資料庫綁定。
  5. 獨立的外部代理。實際你的業務規則并不知道關于外部世界的任何事情。

這篇文章上面的圖試着将以上所有架構整合成一個可執行的想法。

依賴規則

同心圓表示軟體的不同部分。大體上,你走的越遠,軟體的級别更高。外部的圓是機制,内部的圓是政策。

讓這個架構工作的覆寫規則是依賴規則。這個規則說明了源代碼依賴隻能向内。内部圓不能知道任何外部圓的事。實踐中,外部圓裡一些聲明的名字不能被内部圓裡的代碼提到。這包括,函數,類,變量或其他任何軟體實體。

同樣的,外部圓使用的資料格式不應該被内部圓使用,尤其是當這些格式是被外部圓使用的架構生成的時候。我們不想讓外部圓的東西影響到内部圓。

實體

實體封裝企業域範圍的業務規則。實體可以是一個有方法的對象,也可以是一組資料結構和函數。隻要企業裡不同的應用可以使用這些實體就可以。

如果你不是企業級,而隻是寫一個單體應用,那麼這些實體就是應用的業務對象。它們封裝了最通用和高層的規則。當外部變化時它們基本不太會變化。例如,你不會認為這些對象會因為頁面導航或安全方面的變化而改變。任何特定應用的操作都不應該影響實體層。

用例

這層的軟體包含特定應用的業務規則。它封裝并實作了系統的所有用例。這些用例組織了實體中的資料流向,并指揮這些實體使用他們的企業域業務規則來完成用例的目标。

我們不期望這層影響實體。我們也不希望這層會在如資料庫,UI,或其他常用架構這樣的外部變化時被影響。這層隔離了以上關注點。

當然我們期望對于應用操作的變化會影響用例而進一步影響到這層的軟體。 如果一個用例的細節變化了,那麼這層的代碼肯定也會被影響。

接口擴充卡

這層的軟體是一組擴充卡,其将資料轉換成從用例和實體最合适的格式,到對于一些類似資料庫或網站這種外部設施最合适的格式。在這一層,舉個例子,會包含GUI的MVC架構。Presenters, Views,與Controllers都屬于這裡。模型基本就是從controllers傳遞到用例的資料結構,并從用例傳回到presenters和views。

類似的,資料被轉換了,在這層,從對于實體和用例合适的結構,變成對于持久層架構使用的結構。這圈内的代碼不應該知道資料庫。如果資料庫是一個SQL資料庫,那麼所有SQL都應該在這層内,特别是此層與資料庫有關的部分。

這層其他擴充卡也需要将資料從類似外部服務的外部的結構,轉換成用例和實體使用的内部結構。

架構與驅動

最外層主要組合了資料庫,網絡架構這樣的架構和工具。在這層你除了寫一些與内層環通信的膠水代碼,基本不會有其他代碼。

這層是所有細節存在的地方。網絡是細節。資料庫是細節。 我們将這些東西放在外部保證它們不會影響其他部分。

隻有四個圈?

不是的,圓圈是個示意。你可能發現你需要不止4個。沒有規則說你一定要有四個。 實際上,依賴規則一直存在。源代碼依賴一直指向内部。當你向内部移動時抽象的層次在增加。最外部的圓是很低層的具體細節。當你内移時軟體變得更抽象,并封裝了高一級的政策規則。最内部的圓是最普遍的抽象層級。

跨越邊界

在圖的右下方是我們穿越圓圈邊界的示例。它展示了Controller和Presenter與下一層的用例進行通信。注意控制流。它從controller出發,穿過用例,然後在presenter裡執行。也注意下源碼依賴。它們每個都指向内部的用例。

我們通常使用

依賴反轉原則

解決這個明顯的問題。在java這樣的語言中,我們會整理源碼依賴與控制流相反的接口和繼承關系,讓它們從邊界正确的穿過。

例如,用例需要調用presenter。但是,這個調用不能直接進行因為會違反依賴規則。外圈的名字不能被内圈提到。是以我們的用例調用内圈的一個接口(在這個例子裡是Use Case Output Port),并讓外圈的presenter實作它。

架構裡所有的邊界穿越都用這個技巧。我們使用動态多态來建立與控制流相反的源碼依賴,以便于無論在控制流的任何方向都不會違反依賴規則。

什麼樣的資料會穿越邊界

正常來說穿過邊界的資料是簡單資料結構。你可以使用基本結構或簡單的Data Transfer 對象。或者可以友善的進行函數指派的資料。或者你可以打包進一個hashmap,或者将它組裝成一個對象。重要的是穿過邊界的是隔離,簡單的資料結構。我們不想搞變通傳遞實體或資料庫行資料。我們不想資料結構有任何違反依賴規則的依賴。

例如,很多資料庫架構在查詢後傳回一個友善的資料格式。我們可以叫它RowStructure(行結構)。我們不想将這個行機構通過邊界傳遞給内部的圈。這會導緻内部圈需要知道外部圈的内容進而違反依賴規則。

是以當我們在邊界傳遞資料是,要注意其應該是内部圈的格式。

結論

遵從這些簡單規則并不難,并且能幫你減少以後的問題。通過将軟體隔離分層,并遵從依賴規則,你可以建立一個真正可測試的系統,包含了以上所有好處。當任何系統額外部部分過時了,比如資料庫或web架構,你可以容易的替換這些過時的元素。