天天看點

領域驅動設計實戰--戰略模組化

  自從去年接觸DDD以來,閱讀了大量的相關書籍,看了園子裡面很多DDD方面的文章,也在實際項目中也有意無意的使用DDD的一些思想和方法。但總覺得這些知識太分散,沒有給自己思想帶來質的升華。觀摩了園子裡面很多DDD的例子:

[.NET領域驅動設計實戰系列]專題二:結合領域驅動設計的面向服務架構來搭建網上書店 

領域驅動設計實踐

領域驅動設計系列文章彙總

  等等還有很多,這些例子的品質都很高,圖文并茂,一堆讓人流口水的代碼示例。但每次看完例子後,我的迷茫就會又加深一分。因為我覺得很多博文都隻是打着DDD的幌子,還是按自己的原來了解方式寫代碼而已。并沒有給大家深入剖析DDD。别忘了,DDD是領域驅動設計,而不是領域驅動開發。在完成這些例子過程中,太重視出成果,出一個實際的代碼項目,而忽視了設計,即使有些設計,也隻是停留在戰術模組化的階段,并沒有站在戰略模組化的高度來對需求進行分析,通常給出一個UML類關系圖,一個分層架構圖就開始突突突地來一堆代碼,最後還不忘貼兩個漂亮的UI截圖,引起無數粉絲追捧(額~~他們的粉絲中也有我,看來可以把我拉黑了)。用沃恩.弗農大神的話,這樣做隻能算DDD-Lite,離真正的DDD還有段距離。

  于是我有了寫這篇文章的想法。

  所謂的戰略模組化有兩點:界限上下文(Bounded Context)、上下文映射圖(Context Mapping)。

  來看一下它們的定義:

<col>

限界上下文:它是一個限定邊界的環境,在該環境中,每一個模型的概念(包括它的屬性和操作)都具有特殊的含義。它是戰略模組化的核心。

上下文映射圖:通用使用框圖或代碼的方式來展現限界上下文之間的內建關系。

  它們為什麼重要,我以dax.net大大的一個例子來說明,來看一下他的一篇文章EntityFramework之領域驅動設計實踐(三) ,文中内容大概是這樣安排的:

首先介紹他的例子:一個簡易的銷售系統;

給出UML實體框圖;

然後突突的給出一堆代碼;

  這是一個典型的戰術模組化的例子。而且該文中有一句極其誤導DDD新人的話,我不得不吐槽一下:

上面的模型表述了領域模型中各個實體及其之間的關系。我們先不去讨論整個系統的業務會是什麼樣的,我們先看看EF是如何支援實體和值對象概念的。

  它直接讓我們這些技術狂熱愛好者們瞬間偏離了DDD以業務為核心,不依賴具體技術實作的初衷。DDD本來就是引導我們來解決業務上的問題,而不是來讓我們炫耀新技能的。如果隻是介紹EF,那這篇文章是篇合格的文章。但,如果把它作為DDD的文章,那它就是個反面教程。

  很不幸,本文開始的幾個連結也都被我劃到反面教程之列,而且類似的文章園子裡面還有很多。這些文章用來教大家寫代碼可以,但不能作為DDD的教程。

  就像前面說的,我們進行DDD的第一步,不應該是急着去建立實體模型,而應該站在更高的層面去了解需求,劃分領域。這裡我就以dax.net這個簡易的銷售系統為例。首先我們來看看那篇文章中的模型圖(注意:這裡不是用這個模型圖,而僅僅是參考一下用來分析業務,就當這個模型圖是會說話的客戶吧!):

領域驅動設計實戰--戰略模組化

必須坐下來和客戶(就是上面這個模型圖)好好談談了,以下為談話内容:

領域驅動設計實戰--戰略模組化

  把對話内容總結一下,我們可以看出需求業務大概是這樣的:

客戶可以在本系統進行支付,并且可以使用多信用卡支付;

為客戶生成訂單;

在現有的訂單基礎上可以進行退訂;

管理客戶資訊,主要是信用卡資訊管理;

管理産品資訊;

管理産品分類資訊;

     來把這幾條業務進行劃分,得到原始領域結構圖:

領域驅動設計實戰--戰略模組化

  上圖隻是對領域進行了劃分及确定限界上下文,各限界上下文的關系下面會有介紹。這裡共4個限界上下文:客戶上下文,訂單上下文,支付上下文及産品上下文。這裡有以下幾點要特别說明:

通過對領域的劃分,優化了原有系統設計中Item實體即表示訂單項目,又表示産品的混淆的定義。在訂單上下文中,沒有産品這個概念,隻有訂單項,訂單項的屬性資料(主要是産品名稱,單價)會在生成訂單項實體時從産品上下文中的産品擷取;

支付上下文中沒有"客戶"這個概念,隻有"帳戶"這個概念(圖中畫成帳号,在此糾正一下),同訂單中的訂單項,"帳戶"的屬性也會在生成帳戶實體時從其他上下文中擷取,這裡是從客戶上下文中擷取,"帳戶"是客戶的一個子集;

随着業務的增長,可以把退訂業務從訂單管理子域中劃分出來,作為一個單獨子域,這裡暫時先不考慮;

  可以看出,在确定子域及限界上下文後,一些容易混淆的概念會逐漸得到清晰的描述,這樣可以友善開發團隊、業務人員及客戶之間的交流,而且還為我們開發時劃分項目功能提供最直接的依據。

  我們進一步對該圖進行優化。來看一下限界上下文之間的關系,即上下文映射圖:

領域驅動設計實戰--戰略模組化

  圖中的實線連接配接,表示兩端的限界上下文之間存在聯系。線上标注的U/D表示上遊/下遊。通常情況下:上遊的限界上下文會為下遊提供通路接口(或服務),下遊使用一個防腐層擷取從上遊接口傳過來的資料,然後轉化成本限界中使用的實體。

  舉個例子:産品上下文是訂單上下文的上遊。當使用者進行産品選購時,會向訂單裡面添加訂單項(丫的,這裡沒設計購物車~~):

  在僞實作中,添加訂單項的函數AddItem參數用的是産品的id,而不是産品的具體資訊,這樣就可以實作訂單項與産品的解耦。假設我們的産品管理子域是通過WCF用服務的方式實作,防腐層就可以通過通路服務來擷取該産品,然後把它轉化成一個Item。進而實作訂單項的添加。

    DDD方面的代碼實作會在近期放出,敬請期待!

  本文是站在個人了解角度去闡述DDD,在實作過程中也沒有涉及CQRS/AES等概念,隻是想說明一下,如果使用DDD,請站在業務的角度,而不是技術的角度。

  關于學習DDD,強烈建議學習netfocus的ENode架構!

  本文如有冒犯之處還請海涵,歡迎拍磚,不過你要是進行人身攻擊,我也隻能在心裡畫個圈圈詛咒你一下。

作者:李玉寶(李玉寶的代碼人生)

出處:http://www.cnblogs.com/yubaolee/

開源架構:最好用的的權限工作流架構OpenAuth.Net

本作品采用知識共享署名-非商業性使用-禁止演繹 2.5 中國大陸許可協定進行許可。

如您有任何疑問或者授權方面的協商,請給我留言。