Microsoft – Spain團隊有一個很不錯的“面向領域多層分散式專案”案例:Microsoft – Domain Oriented N-Layered .NET 4.0 App Sample(在本系列文章中,我使用NLayerApp作為該專案的名稱進行介紹),在codeplex上的地址是:http://microsoftnlayerapp.codeplex.com/。它是學習領域驅動設計(DDD)的一個非常不錯的案例專案。

該文章翻譯自專案的使用者手冊~

1.-  N層應用架構
1.1.- 層(Layers)vs 層 (Tiers)

  這兩個詞在業界歷史上都是廣為採用並且可以替換,但是我們覺得區分它們的是有用的。
  從我們的角度來看,區分Layers和Tiers的概念是很重要的。
  Layers指的是元件和功能模組的劃分,而不是在不同伺服器或者地方的元件的物理劃分。相反,Tiers指的是
元件和功能模組在不同伺服器上的物理分佈,包括網路拓撲和遠端地點。雖然Layers和Tiers用了相同的層級的
名字(表現,服務,業務和資料),最好別把它們弄錯了。請記住只有Tiers表示物理分隔,場用來表示物理分佈
模式,例如“2層”、“3層”、“多層”。
  下面我們展示一個3層(Tier)方案和多層(Layer)方案的圖,來看一下剛才討論的分別:

  最後,需要注意的是,因為所有的應用都有一定的複雜性,所以都應該用多層Layer的邏輯架構來進行邏輯構建;
然而,不是所有的應用都應該使用多Tier模式,除非需要Tier上的物理劃分,這在web應用裡很常見。
  3Tier架構已經很老了,但是依然可以應用。當然,內部技術已經發生很大的變化。實際上,3Tier的圖是微軟
大約在1998年的一張圖。

1.2.-  層 (Layers)

背景

設計有相當數量不同抽象級別上元件的複雜商業應用。

問題

問題在於怎樣構建一個支撐複雜操作需求的應用,具有高可維護性,可重用性,可擴充套件性,健壯並且安全。

相關問題

構建應用時,應該考慮下面的的要求:

-  解決方案中的一部分改變時應該對其他部分應該影響最小,減少修復缺陷的工作量,提高應用的可維護性
和整體的靈活性。

-  職責分離(例如分離使用者介面和業務邏輯,業務邏輯和資料庫訪問)也可以增加靈活性、可維護性和可擴充套件性。

-  為了確保穩定性和質量,每一層必須有單元測試。

-  在應用的不同模組或者不同的應用裡,某些元件必須是可複用的。

-  開發團隊可以開發解決方案的不同部分,並不依賴於其他團隊的部分,為此,應該通過明確的介面來互動。

-  單獨的元件必須高內聚。

-  不是直接關聯的元件必須鬆耦合。

-  解決方案的不同元件可以在不同的時間獨立部署、維護、升級。

  層(Layers)被視為構成應用或服務的水平堆疊的一組邏輯上的元件。它們幫助區分完成不同任務的元件,提供
一個最大化複用和可維護性的設計。簡言之,是關於在架構方面應用關注點分離的原則。
  每個頂級的邏輯層可以有若干個子層,每個子層執行特定的任務。大部分解決方案中通過在子層中使用通用的組
件,建立被承認的模式。我們可以使用這種模式作為我們設計的模型。
  劃分應用中單獨的有不同角色和功能的層增加可維護性。這也允許不同型別的部署,同時提供了一個各種型別
功能模組和技術的清晰的劃分。

  層的基本設計

  首先,請記住當提到層的基本設計時,我們不是講的DDD多層架構。我們講的是傳統的多層架構(比DDD多層架構簡單)。
  正如已經陳述的,每個解決方案的元件必須分隔到不同的層。每層的元件必須內聚而且有大約相同的抽象級別。每個
一級層應該和其他的一級層鬆耦合:
  從最底層的抽象級別看,例如第1層。這是系統的基礎層。這些抽象的步驟是一步一步的最後到最頂層。
.


  多層應用的關鍵在於對依賴的管理。傳統的多層架構,層內的元件只能和同級或者低階層的元件互動。這有利於
減少不同層內元件的依賴。通常有兩種多層架構的設計方法:嚴格和靈活的。


  “嚴格的層設計”限定層內的元件只能和同一層、或者下一層的元件通訊。在上圖中,如果我們用這種設計,第N層
只能和第N-1層互動,N-1層只能和N-2層互動,等等。


  “靈活的層設計”允許層內的元件和任何低級別層互動。這種設計中,第N層可以和N-1,N-2層互動。
  這種設計由於不需要對其他層進行重複的呼叫,從而可以提高效能。然而,這種設計不提供層之間的同層隔離級別,
使得它難以在不影響多個高階層的時候替換一個低階的層。
  在大型複雜的解決方案裡,需要引入多個軟體元件,在同一級別的層(抽象)裡有許多元件是很常見。這樣,不是
內聚的。在本例中,每一層必須被分隔成兩個或者更多的內聚子系統叫做模組,垂直分佈在每個同級層。模組的概念
會在這章中的後面作為建議架構的一部分介紹。
  下面的UML圖展示了層的組成,相應地在多個子系統內的情況。


測試的注意事項

  在正確實現測試後,多層應用極大的提高了能力。

- 由於層之間是通過定義明確的介面進行互動這一事實,很容易為各層新增替代的實現(例如 Mock  and Stubs)。
這使得對一個層的單元測試可以在其依賴層沒完成的情況下進行,或者是要更快的執行一個大型的單元測試,但是
訪問依賴層時很大程式的減慢了執行速度。而且,用mocks and stub隔離層元件限制了測試成功或失敗的原因。
因此我們可以在不考慮外部因素的情況下真正的測試內部邏輯。這是真正的單元測試。不同於其他,我們將進行
整合測試。如果我們用基類("分層的超型別“模式)和基介面(”抽象介面“模式),由於它們進一步限制了層間的
依賴,這種能力會增強,由於介面提供了更先進的解耦技術,所以使用介面非常重要,將在後面介紹。

-  因為高層的元件只能和底層的互動,在單獨的元件上進行測試是很容易的。這有助於分離單獨的元件進行正確的
測試,可以更容易的更改低階層的元件而對應用影響很小(只要滿足了介面要求)。

使用層的好處 

-  功能容易確定位置,解決方案也就容易維護。層內高內聚,層間鬆耦合使得維護/組合層更容易。

-  其他的解決方案可以重用由不同層暴露的功能。

-  當專案按邏輯分層時,分散式的部署更容易實現。

-  把層分佈到不同的物理層可以提高可伸縮性;然後這一步應該進行仔細的評估,因為可能對效能帶來負面影響。

參考
 
Buschmann, Frank; Meunier, Regine; Rohnert, Hans; Sommerland, Peter; and Stal, 
Michael. Pattern-Oriented Software Architecture, Volume 1: A System of Patterns. 
Wiley & Sons, 1996. 
 
Fowler, Martin. Patterns of Application Architecture. Addison-Wesley, 2003. 
 
Gamma, Eric; Helm, Richard; Johnson, Ralph; and Vlissides, John. Design 
Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, 1995.

1.3.-  應該遵從對基本設計原則

  設計系統時,一些基本對設計原則可以幫你構建一個經過實踐驗證的架構。下面的重要原則有助於減少維護費用,
最大限度地提高可用性並且提高可擴充套件性。
 
1.3.1.-  "SOLID‟ 設計原則

  SOLID是從下面對短語/原則的英文首字母:

我們總結了以下的設計原則:

  單一職責原則:每個類都應該有一個唯一的職責或主要特徵。就一個類而言,應該僅有一個引起它變化的原因。
這個原則的一個結果就是,通常類應該儘量不依賴其他對類。

  開閉原則:一個類必須對擴充套件開放,同時拒絕修改。即,不需要修改類的程式碼就可以擴充套件類的行為。

  里氏代換原則:子類必須可以被它對基類替換。這源於基於抽象的程式行為不應該隨具體的實現而改變的事實。
程式應該使用抽象,而不是具體的實現。我們將在後面看到這條原則與依賴注入和替換實現同一介面的類緊密相關。

  介面分離原則:類的介面實現不應該強制實現無用的方法。這意味著介面應該具體取決於使用它們的類,應該使用
小的介面而不是一個大的介面。針對不同的消費類,類應暴露不同的介面以提供不同的介面要求。

  依賴倒置原則:抽象不應該依賴於細節,細節應該依賴於抽象。類之間的直接依賴應該用抽象替換允許自頂向下的
設計,而無需首先設計較低層。

1.3.2.-  其他重要的設計原則
  
  元件的設計必須高內聚:不要在元件裡增加不相關的功能。例如,避免把屬於領域模型的業務邏輯放到資料訪問層。
功能內聚後,我們可以建立有多個元件的程式集,置於應用中相應的層中。因此這條原則與多層模式和單一職責原則
密切相關。

  把跨領域的程式碼從應用的邏輯抽象出來:跨領域的程式碼指的是水平方面的程式碼,例如安全性,操作管理,日誌,規範
等等。在程式中使用具體的實現,可能導致在未來難以擴充套件和維護。面向切面程式設計(AOP)的原則和這部分有聯絡。

  關注點分離:把應用程式劃分成不同部分並且最大限度地減少這些部分之間的重疊功能。關鍵點是最小化的互動的地方
以實現高內聚低耦合。然後,錯誤的分離功能點可能導致高度的耦合和系統特性的複雜性。

  不要重複自己:想做的必須在系統的某一個部分。例如在一個應用設計中,一個特定的功能應該只在一個元件裡實現;
這個功能不能在別的元件裡實現。

  最小化自頂向下設計(前期設計):只設計需要的而不要過度設計,考慮敏捷設計原則。

1.4.-  DDD架構的趨勢方向(領域驅動設計) Orientation  to DDD architecture trends 

  這篇架構框架的目的是提供了統一的基礎和一套具體應用型別“複雜業務應用”的使用者手冊。這種型別的應用的特點是
有一個相對長的生命週期並且可以承受相當數量的變化。在這些應用中持續的維護是非常重要的,包括更新/替代新的技術
和框架,例如換新版本的O/RM。目標是所有這些變化的對程式的影響最小。改變基礎設施層的技術不應影響高級別的層。
具體而言,“領域模型“層應該受到最小程度的影響。
  複雜應用中,業務規則的行為(領域邏輯)經常變化,所以保留對領域層修改,進行簡單且獨立的方式測試是非常重要的。
實現這個重要目標需要領域模型(邏輯和業務規則)和系統其他層(表現層,基礎設施層,資料持久化層等)之間的最小耦合。
  應用架構的趨勢是朝向實現層之間的耦合,尤其對於領域模型層來說。作為領域驅動設計的一部分,面向領域的多層架構
專注於這一目標。

  重要:
  領域驅動設計不僅僅是一種建議的架構;也是一種處理專案的方法,一種工作方法,基於領域專家(業務專家)的知識
識別通用語言的重要性,建立正確的模型等等。然而,這些方面不在本手冊裡;我們的範圍僅限於邏輯和技術架構內的典型的
架構模式和建議的.NET實現。請參閱面向領域設計相關的書例如(“Domain-Driven Design‟, by Eric Evans)和其他更詳細的
資訊關於如何應用面向領域設計到你的專案的生命週期裡

  您不應該採用面向領域的N-層架構的原因

  如果應用相對簡單,在應用的生命週期裡不會有基礎設施技術的改變,尤其是業務邏輯很少會變動,那麼你的解決方案就
可以不按照該手冊裡介紹的方法架構。相反,你應該考慮快速應用程式開發(RAD)。快速的實現在構建簡單的組建和層的解耦
不重要的適合非常有效,強調的是生產力和上市時間。通常情況下,這些種應用程式是資料驅動的應用程式,而不是領域驅動
設計。

 應該採用面向領域的N-層架構的原因
 
 當業務行為會隨時間變化時,應該考慮使用面向領域的N-層架構。在這些情況下,領域模型對每次改變需要的工作會減少,
應用使用更耦合的方式會有更低的總擁有成本(Total  Cost  of  Ownership)。簡言之,在軟體的單獨區域封裝業務行為
會顯著降低修改應用的時間。這是因為修改只在一個地方進行,並能很方便地進行隔離測試。能隔離領域模型的程式碼很大的
減少了在應用其他地方進行修改(有可能帶來新的問題)的情形。如果你想減少並提高穩定週期和解決方案的除錯,這是非常重要的。

  領域模型有效的情形
  
  業務規則的一些可操作說明可以用來確定是否實現領域模型。例如,在業務系統中,一個規則指明一個客戶不能有超過2000
元的拖欠款,這個規則應該屬於領域模型。實現這樣的規則通常涉及一個或多個實體,必須在不同用例的背景下進行評估。
  從而,一個領域模型會有很多的這樣的業務規則,包括一些可以取代其他規則的規則。例如,關於上述規則,如果該使用者是個
特殊的賬戶,拖欠款可以更高,等等。 
  簡言之,應用的業務規則和用例越重要,越應該適合領域模型的架構而不是簡單的
在一個面向資料的應用中定義的實體關係。
  最後,為了將通過將記憶體中的物件集(實體物件/圖)儲存到一個關係型資料庫,我們可以使用ORM型別來進行資料持久化,
例如Entity Framework 或者 NHibernate。然而,重要的是將這些具體的資料持久化技術(基礎設施技術)和應用的業務行為
很好地分離開。這需要一個應用鬆耦合方式的N層架構,我們將在後面看到。

1.5.-  分散式領域驅動設計(DDDD)

  四個D?是的,很顯然, DDDD在領域驅動設計上考慮到分散式系統的演變/拓展。 Eric  Evans,在他關於領域驅動設計的書裡
幾乎沒有提到分散式技術的相關主題,而書中由於主要關注的是領域。然而,在許多情況下。我們需要分散式系統和遠端服務。

  實際上,由於我們從一開始就考慮到分散式服務,本文裡的N層架構是基於分散式領域驅動設計的,同時我們用建議微軟的技術
進行實現。
  
  最終,分散式領域驅動設計使我們更接近分散式、高擴充套件性、甚至接近雲端計算的情景。我們會在最後一章進行闡述。

2.- 面向領域的N層架構設計

  如上述,我們要弄清楚所講的是面向領域架構,而不是所有關於領域驅動設計的東西。如果要討論面向領域架構,除了架構
還應該討論設計過程,開發團隊的工作方式,通用語言等等。將會在此簡要地討論這幾個方面。本手冊的目的是專注於領域
驅動設計的架構以及如果用微軟的技術實現。因為有很多不錯的書的討論領域驅動設計,所以我們不打算在這裡詳細說明和解釋。

  本節提出了我們建議的面向領域的N層架構和層的總的定義。

2.1.- 表現層,應用層,領域層,基礎結構層  

  在最高和最抽象的級別,系統的邏輯架構檢視可以看做一組在內部有關聯的幾個層,類似於以下的圖(按照領域驅動設計的樣式)


在面向領域架構中,關鍵是要清楚界定和分離領域模型層和其餘的層。這是領域驅動設計的先決條件。“一切都必須圍繞領域,
領域模型層必須和基礎結構技術分離“。
  因此,複雜應用應該分層。應該在各個層內進行設計。設計必須內聚且和系統中的其他層明確界定邊界,應用標準的架構模式
使得依賴大多基於抽象而不應是層直接依賴別的層。領域模型的所有相關程式碼都應集中在一層,和其他層的程式碼分離出來。領域
不能有取得、儲存領域模型、管理應用程式的任務等等的程式碼。領域必須專注於表達領域模型(資料和邏輯)。這使得領域模型
可以逐漸變得豐富和清晰來表現重要的業務知識,並在應用程式中實現的業務需求。
  把領域層從其餘各層分離開,可以讓每層的設計更加乾淨。分離的層是更容易維護,因為經常在不同的時候進行不同需求的修改。
例如,基礎結構層在技術升級時進行改造。另一方面,領域層只會在業務邏輯變化的時候改變。
  此外,層的分離有助於分散式系統的部署,它允許不同的層部署在不同的伺服器上,以便最大限度地減少通訊和提高效能(引用
M.  Fowler)。但是這種層的分散式部署取決於特定的應用需求(安全性,可伸縮性等等)

  層之間元件的鬆耦合是必不可少的。應用的每層都有一系列的元件構成。這些元件應該內聚,但是層之間應該鬆耦合來支援單元
測試,模擬和重用來減少維護的影響。主要層的設計實現鬆耦合想在後面詳細介紹。

2.2.- 面向領域N層架構
  
  這種架構的目的是按照領域驅動設計的模式,簡單清晰的構建多層的複雜業務應用程式。當用N層模式時,在應用中不同的內部層
和子層可以是不同的。
  當然,這個特殊的多層架構是可以根據每個專案或偏好來定製的。我們只是提出一個建議的架構基礎,可以根據需要和需求進行
調整和修改。

  特別的,建議的“面向領域N層”應用的層圖如下:


- 表現層
   o  視覺化元件子層(檢視)
   o  使用者介面邏輯子層(控制器及類似)

  - 分散式服務層(Web services) 
   o  瘦Web services門面模式
  - 應用層 Application  layer 

   o  應用服務(協調任務和用例)
   o  介面卡(格式轉化等)
   o  工作流子層(可選)
   o  應用層基類(層超型別模式)

  -  領域模型層
   o  領域實體和聚合 
   o  工廠
   o  領域服務
   o  查詢規範(可選)
   o  倉儲介面/契約 
   o  領域基類(層超型別模式)

  -  資料持久化層
   o  倉儲實現
   o  基類(層超型別模式)
   o  資料邏輯模型和對映
   o  O/RM 技術基礎設施
   o  外部服務的服務代理

  - 架構的橫切元件
   o  安全性,運營管理,監控,郵件系統等方面。

  這裡簡單地說明這些層,會有一整章來分別介紹每一層。在這之前,有趣的是,從高層次的角度來看,層之間的互動的
很像我們這麼劃分的原因。
  領域驅動設計的主要前提和來源是Eric  Evans的“領域驅動設計- 應對軟體的複雜性“,書裡描述和解釋了建議的N層架構
高層次的圖:


值得注意的是,在某些情況下是直接訪問其他層的。就是說,沒有任何理由層直接的關係必須是單向的,雖然這取決於每個應用
的情況。為了說明這種情況,下面我們展示了Eric Evans之前的一個圖。我們修改了這個圖,增加了一些細節,於低級別的子層
和元素有關。


首先,我們看到基礎結構層,該層為很多不同的環境(伺服器和客戶端環境)提供功能。基礎結構層包含和技術/基礎設施相關的
所有東西。還有一些基本概念,其中包含如資料持久化(倉儲等等),也有例如安全性,日誌,監控等等的橫切主題。甚至還包含
具體的影象方面的庫。由於軟體環境的巨大差異和資料訪問的重要性,在本文的架構中,我們會把資料持久化層和基礎結構的其他
部分(通常是橫切基礎結構層)分開,這樣這部分可以被任何層已橫切的方式使用。
  另外一個我們遇到的情況是,不只是通過一條單一路徑訪問一些層。特別地,必要的時候我們可以直接訪問應用,領域,橫切層。
例如,我們可以從表現層直接訪問應用層,領域層或者橫切基礎結構層。然而,訪問資料持久化層和層內的倉儲物件,通常建議通過
應用層的協調物件(服務)來進行,因為應用層的物件是協調大部分基礎設施物件的模組。
  值得一提的是,這些層的實現和使用應該領靈活。將來也許圖中會有更多的組合箭頭。所以,不用在所有的應用有使用一樣的方法。
  此章的後面我們會簡單介紹每一層和子層。也會提出了一些關於如何定義和實現這些層的總的概念。(例如層間的鬆耦合,不同物理
層的部署等等)
  接下來,在下面的章節我們會詳細解釋每個高級別的層。

  表現層

  這層的作用是給使用者展示資訊和解釋行為。  表現層的元件實現使用者與應用互動的功能。
一般建議用MVC,MVP或者MVVM這樣的模式來分隔這些元件為子層。

o  可視元件子層(檢視): 裡面的元件提供終端使用者使用應用的能力。裡面的元件用視覺化控制元件顯示資料
以及從使用者取得輸入資料。

o  控制器 : 從圖形介面分離出元件有助於使用者互動。這使得控制元件、頁面不包含處理流程和狀態管理邏輯的程式碼,
可以讓我們脫離介面來重用程式碼的邏輯和模式。對錶現層的邏輯進行單元測試頁很有用。控制器通常基於MVC模式
及其衍生品。

分發服務層(Web服務)

  當應用作為服務提供者為遠端應用或者當表現層位於遠端端時(富客戶端,RIA,OBA應用等等),業務邏輯
通常通過分發服務層釋出。這層提供了提供了一種基於通訊通道和資料資訊的遠端訪問。需要注意的是,本層
應該越薄越好而且不應該包含業務邏輯。

應用層

  這層是建議的面向領域架構中的一部分。這層呼叫領域層和基礎結構層(資料持久化等等)來完成應用的用例。
  實際上, 應用層中不應有領域規則或者業務邏輯;應該執行程式的呼叫,而這是不用解釋給領域專家或者使用者
的。在應用層中,我們實現協調應用的“通道”,例如事務,執行單位操作,呼叫應用程式的任務。應用層中可以
實現其他的功能,比如應用優化,資料轉換等等,都可以稱為應用程式的協調。最終的操作,將會被委派到低層
物件。應用層中不能有表示內部業務邏輯的狀態,但可以有展示給使用者的表示執行程式任務的狀態。
  由於像為領域模型的一個門面,應用層有點類似於“業務外觀”模式。但是,應用層不僅是簡單的呼叫領域。包括
在這一層的功能有:

  -  協調資料持久層的倉儲物件的呼叫

  -  分組/合併更高層需要的實體資料,實現減少遠端呼叫的次數來增加效率。傳送的資料為資料傳輸物件(DTO),
  將實體和資料傳輸物件進行相互轉換的叫做DTO介面卡。
 
  -  響應使用者介面的操作的操作,執行領域的運算,呼叫相應的資料訪問運算。

  -  維護應用相關的狀態(不是領域物件的內部狀態)。

  -  協調領域和基礎結構層的操作。例如,執行一個銀行轉賬需要從倉儲獲取資料,然後用領域物件的轉賬業務邏輯
  ,最後可能給相關人傳送郵件。

  -  應用服務:注意這個服務不是Web服務。首先,服務的概念在很多層裡都有:應用層,領域層甚至基礎設施層。
  服務的概念是一組類,操作若干低層的類。因此,服務一般用來協調底層的物件。
     應用服務,就是一般來協調其他底層的服務(領域服務或橫切基礎結構層服務)。例如,應用層可以呼叫領域層
  來執行在記憶體中建立一個訂單物件。當領域層執行這樣的業務操作(多數改變的是記憶體中的物件),應用層會呼叫
  基礎設施層的倉儲來執行資料來源的操作。

  -  業務工作流(可選):一些業務流程包括幾個步驟,這應該按照具體的規則來實現,而且通常比較耗時。這種
  業務流程應該由業務流程管理工具的工作流實現。

  應用層也可以通過Web服務層作為一個門面釋出,這樣就可以被遠端呼叫。

領域層

  領域層負責展示業務/領域概念,業務流程的狀態和領域規則的實現。應該包含展示業務流程的狀態。
 
  領域層是軟體的心臟。
 
  為此,領域曾的元件應該實現系統的領域核心功能,封裝所有相關的業務邏輯(領域驅動設計術語裡的領域邏輯)。
基本上,是一些用方法實現領域邏輯的類。按照面向領域的N層架構模式,領域層應該對資料持久化細節透明。
  通常我們可以在領域層裡定義下面的元素:

  領域實體:領域物件包含資料和邏輯,用來在層間傳輸實體資料。領域驅動設計的一個基本特徵是,領域實體包含領域
邏輯。例如在銀行賬戶實體中,存款的操作應該在賬戶實體內部進行。也可以包括資料驗證,屬性計算,和其他實體的關係
等等。最後,這些類表達了現實世界中的實體。另一方面,應用內部的實體是在記憶體中的有著資料和邏輯的物件。如果只用
實體來做資料傳輸,沒有相關的邏輯,我們會陷入最初由Martin Fowler描述的貧血領域模型的反模式,另外,推薦使用POCO
(Plain  Old  CLR  Objects)實體,一種不基於任何資料訪問技術或框架的類。該設計(持久化透明)的最終目標是領域類不能有任何直接資料
訪問技術的引用。
  由於領域類必須獨立於任何基礎結構技術,領域類必須放在領域層內。所有的情況下,實體是貫穿架構中最多層的物件。
  關於領域驅動設計的定義,並且根據 Eric Evans的“一個由標示符定義的物件叫做實體”。實體是領域模型的基本概念,
必須仔細辨認和設計。在一些應用中的標識在別的應用可能不是。例如,地址在一些系統可能不是標識,但在其他的系統,
例如電力公司,客戶的地址很重要,應該作為一個實體。
  聚合:聚合是有清晰邊界的實體和值型別物件的組合。將會在領域模型層的章節具體解釋聚合。
  工廠:當建立一個聚合很複雜時,用工廠來建立聚合就很有用。將會在領域模型層的章節具體解釋工廠。
  領域服務:在領域層,服務是一些組織執行領域邏輯的類。這些類一般不應該有領域相關的狀態(無狀態類)。
這些類用來協調領域實體的操作。典型的領域服務同時關聯幾個實體。但也可以有負責只和一個根實體互動的服務。
關於倉儲,一般由應用層呼叫,尤其當執行事務或者使用工作單元模式(UoW)的時候。但有時需要根據領域邏輯
來從倉儲獲取資料,這種情況(一般是查詢),可以在領域服務中使用倉儲。
  倉儲契約:顯而易見的是,倉儲不在領域中實現,而是基礎結構層的一部分。然而,介面(契約)必須屬於領域。
契約表明了倉儲應該提供什麼來滿足領域,而不管倉儲內部是如何實現的。這些介面(契約)不應該知道使用的技術。
另一方面,實現這些介面的類會用某種技術來實現。因此重要的是倉儲介面(契約)必須在領域層定義。這是按照
Martin  Fowler 的分離介面模式,推薦的面向領域架構的模式。從邏輯上講,為了能夠遵守這條規則,領域實體和
值型別需要是POCO型別;即負責維護實體和資料的物件必須對資料訪問技術透明。必須考慮到領域實體最終是倉儲
傳遞是引數“型別”。
  
  資料持久化基礎結構層

  這層提供持久化和訪問資料的功能。資料可以是自己的系統或者外部系統的。因此,資料持久化層給高階的層
公開資料訪問。這種公開應該是用過鬆耦合的方式。

  - 倉儲的實現:倉儲,通用的術語是”在一個組內表示某一特定型別的所有的物件“(Eric  Evans的定義)。
實踐的方面,一個倉儲通常是一個用某種技術完成持久化和資料訪問操作的類。通過這樣做,我們把資料訪問功能放在
一個地方,這樣可以更方便和直接的維護和配置應用。通常,我們為每個根實體建立一個倉儲。根實體有時候只有一個
實體,有時候可以是一個複雜的聚合,包括很多實體,值型別。
    應該通過在領域層的介面訪問倉儲,這樣可以在領域層來進行分離倉儲的單元測試,或者用另一種技術實現倉儲
而不影響領域層。
    倉儲的關鍵是使得開發人員可以集中注意力在領域邏輯上,通過倉儲契約來隱藏資料訪問的實現方式。這個概念
叫做持久化透明,這意味著領域模型完全不知道資料的儲存和查詢方式。
    最後,需要區分資料訪問物件和倉儲。主要的區別是資料訪問物件在儲存上直接進行持久化和資料訪問操作。
然而,倉儲先在記憶體中標記/儲存物件,以及要進行的操作,但是會在稍後才進行真正的執行。這就是在應用層
這些持久化/資料訪問操作會在一個事件中一次完成。通常基於工作單元模式,這會在下面的章節詳細介紹。工作單元
模式可以提升應用的效能,也可以減少不一致的可能性;在高擴充套件系統裡,減少由於事務引起的資料庫鎖數目。

  - 基本元件:大部分的資料訪問任務需要共通的邏輯,可以抽出並在一個單獨的可重用的元件裡。這有助於簡化
資料訪問元件,尤其是減少需要維護的程式碼量。這些元件可以有基類或者工具類的方式實現,可以在不用的專案重用。
這個概念是一個非常有名的由 Martin Fowler定義的分層超類模式,主要說的是“如果把類似的類中的通用行為抽象到
基類裡,會減少很多重複的程式碼”。使用這個模式純粹是為了方便但不要分散關於領域的注意。
    “分層超類模式”可以在任何型別的層中使用。

  -  資料模型/資料對映:這是有對映領域實體模型到資料庫表的ORM。按照選擇的ORM工具,對映可以是基礎程式碼或者
視覺化模式。
   
  -  代理服務:有時候業務元件需要使用外部/遠端服務提供的功能。在這些場景,需要實現一個管理通訊並且對映資料
的元件。代理服務隔離特定的介面,這樣可以模擬外部的服務來進行單元測試,甚至用另一個服務替換而系統的核心部分
不受影響。

  橫切基礎結構層

  這層給其他各層提供了通用的技術能力。最後,這層用它的功能“堆積木”。應用中有很多工是要在不同層裡實施的,
可以被各層使用的橫切面。最常見的橫切面有:安全性(身份驗證,授權和驗證),運營管理(策略,日誌,跟蹤,監測,
等等)。這些方面會在下面的章節詳細介紹。

  - 橫切基礎結構服務:服務的概念也是關於該層的。這些服務負責組織和協調基礎結構動作,例如傳送郵件,監控安全事件,
運營管理,日誌,等等。這樣,這些服務負責組織所有的技術方面的事宜。

  - 橫切基礎結構物件:根據橫切基礎結構物件的型別,來實現需要的特定物件,不管是安全事件、跟蹤、監控等等
需要用指定的API。這些橫切基礎結構層覆蓋很多方面,很多和服務質量(QoS)有關,實際上和具體的技術相關。更多
的詳情將在一章中討論橫切面。

  服務是一個在不同層通用的概念

  由於服務中在DDD架構的不同層都出現,我們在下面的表中總結了DDD中服務的概念:

  表2:面向領域架構的服務
 
  我們已經看過了所有的層,它們都可以有服務。由於服務在不同地方都有它的意義,我們可以比較方便的來看
服務在DDD中的總體方面。

  首先,需要注意的是服務並不是為了進行遠端呼叫的Web服務。Web服務可以位於分發服務層,可能給遠端呼叫
或者應用、領域層使用較低層的服務。

  DDD服務的概念,最乾淨實用的設計中,包括了層內不屬於同一物件的操作(例如對多個實體的操作)。這種情況
下我們可以把這些操作組織為服務。
 
  這些操作自然是由對多個物件的活動組成。由於程式設計模型是面向物件的,我們也應該把操作組織成物件。這些物件
叫做服務。

  這麼做的動機是,如果把這些操作作為原來物件的一部分,會歪曲真實物件的定義。例如,實體在邏輯上應該是和
內部的機制例如驗證該實體等相關,但不應該把實體本身看做一個整體。例如,一個發動機實體執行有關發動機的
行為,而不應該和發動機是怎麼製造的相關。同樣地,實體類的邏輯不應該管理它的持久化和儲存。

  此外,服務是一個或一組作為介面提供的操作。服務不能封裝狀態(必須是無狀態的)。這並不意味著實現的類必須
靜態的;一般情況下回是一個例項類。服務是無狀態的意味著服務端程式可以使用任何該服務的例項,而不用管每個
物件的狀態。更重要的是,服務的執行可以使用甚至更改全域性資訊。但是服務本身沒有狀態來控制它自己的行為,
不像實體。服務這個詞在服務模式裡描述的提供了:服務可以提供給呼叫它的客戶端的是,強調每一層與其它物件的關係。

  服務一般由操作命名,而不是物件名。因此,服務和用例關聯,而不是物件,即使有特定操作的抽象定義(例如,
“轉賬服務”和動作“從一個銀行賬戶轉錢到另一個”相關)。

  為了解釋這一點,怎樣在不同的層中區分服務,下面舉了一個簡單的銀行情景:

  應用層:應用服務”銀行服務‟

             -接收並轉化輸入資料(例如把DTO轉化為實體)。
    
    -提供領域層轉賬的資料,以便在領域層處理業務邏輯。

    -呼叫基礎結構層的持久化物件(倉儲庫)來儲存領域層的變化。

    -決定是否要用橫切層的服務傳送通知。
           
    -最後,實現了所有“協調技術的管道”,使領域層只負責清楚的表現邏輯。

  領域層:領域服務”銀行轉賬‟(動詞轉賬)

             -呼叫例如銀行賬戶的實體的方法。
    
    -提供了業務操作結構的確認。

  橫切層:橫切服務"傳送通知"

              -協調郵箱傳送或者其他型別的通知,呼叫基礎結構的元件。

  到目前為止,按照本章中所有的解釋,你可以推斷出按照商業應用開發中什麼是第一條準則:

  表3:DI設計原則

  原則 #:D1. 複雜應用的內部架構應該設計成為基於多層應用的架構並且面向領域。
 
o 規則 

- 通常,這條規則可以應用到有很多領域邏輯和長生命週期的複雜商業應用。

  何時使用面向領域的多層架構
 
- 應該被使用到複雜的,有很多變化的業務邏輯的,有著相對長生命週期需要後期維護的商業應用。

  何時不應使用面向領域的多層架構

- 在小型的完成後幾乎不會有改變的應用。這類的應用有一個相對短的生命週期,開發速度優先。
這類應用推薦使用快速開發技術。然後,實現更復雜耦合的元件時會有缺點,這將導致在應用有相對
較差的質量。因此,技術的升級和未來的維護費用會隨應用是否有大的改變而定。

  使用多層架構的優點

-  在一個組織中,不同的應用使用結構化,同質活類似的開發流程。

-  簡單的應用維護,由於不同型別的任務總是位在架構的同一地方。

-  改變應用的物理部署時更容易。

  使用多層架構的缺點

-  在非常小的應用中,增加了過多的複雜性。這種情況下可能是過度設計。但這種情況下是不太
可能在有一定複雜性的商業應用出現。

參考
 
Eric Evans: Book “Domain-Driven Design: Tackling Complexity in the Heart of 
Software” 
 
Martin Fowler: Definition of „Domain Model Pattern‟ and book “Patterns of 
Enterprise Application Architecture” 
 
Jimmy Nilson: Book “Applying Domain-Driven-Design and Patterns with examples in 
C# and .NET” 
 
SoC - Separation of Concerns  principle: 
http://en.wikipedia.org/wiki/Separation_of_concerns 
 
EDA - Event-Driven Architecture: SOA Through the Looking Glass – “The 
Architecture Journal” 
 
EDA - Using Events in Highly Distributed Architectures – “The Architecture Journal” 
 
  儘管這些層最初是為了覆蓋多層應用架構的大部分,對於特定的應用,基礎架構對引進新的層和進行
定製式開放的。
  同樣地,完全實現建議的層不是強制的。例如,在某些情況Web服務層可能不需要實現,因為不需要
遠端訪問,或者你可能不想實現某種模式,等等。

2.3.- 元件之間解耦

  需要注意的是應用的元件不應該只在層間定義;我們還應該特別注意元件之間怎樣互動,那就是,它們
怎樣被使用,尤其是一些物件被另外的物件例項化。
  通常,應該在所有屬於不同層的物件之間解耦,,由於在應用中有一些層我們想以解耦的方式進行整合。
這是基礎結構層的大多數情況,例如資料持久化層,可能和特定的ORM方案結合,或者是一個特定的外部後端。
簡言之,為了在層中實現解耦,不應該直接例項化層中的物件(例如,不直接例項化倉儲物件或其他和特定
技術相關的基礎結構層的物件)。
  這點本質上是關於任何型別物件的解耦,不管他們是不是領域層裡的物件,或者表現層裡的元件可以模擬
Web服務的功能,或者在持久化層裡能夠模仿外部Web服務等等。在這所有的情況下,應該用解耦的方法操作
為了用最小的影響可以用模擬的實現替換真實的實現。在所有這些例子中,結構式非常重要的方法。
  最後,我們在應用中實現“使用最先進技術”的內部設計:“應用的體系結構都採用解耦的方式構建,讓我們
可以在任何地方和事件增加功能。不只是在層之間使用解耦。”
  只在層之間進行解耦可能不是最好的方法。例如在領域內部增加不同物件的集合(例如,一個客戶端包括
垂直模組)解釋了上述。
  在該架構指南的示例應用中,我們選擇為應用中的層的大部分物件解耦。所以這種方法是完全可用的。
  解耦的技術基於依賴倒置原則,這闡明一種特殊的解耦方式,即將傳統的面向物件的依賴關係反轉。目標是
讓層獨立於具體實現的其它層,因此和實現的技術無關。

  依賴倒置原則的如下所述: