接觸領域驅動設計DDD有一年多的時間了,中間看過很多書,參與過一些討論(ENode QQ羣)。目前對DDD的認知還停留在理論階段,因此對領域建模很是感興趣,這裏說的建模是指以DDD的思想爲指導再加上DDD的工具,好比聚合、實體、值對象等等。html
昨天有羣友分享了一個建模的案例,我想在這兒記錄下來,有兩個目的:一、爲本身學習DDD儲存素材 二、鍛鍊本身寫文章的能力併發
關於DDD中的一些概念,請見以下文章連接:app
http://www.cnblogs.com/ivanzheng/p/5533162.html(Ivan翻譯)框架
http://www.cqrs.nu/Faq(這是原文連接,英文好的能夠直接讀這個)ide
你們確定都據說過視頻課程網站吧,好比慕課網。這兒要說的場景跟這個網站的後臺管理比較相關。好比,課程的做者能夠上傳一些課程視頻(Video)到該網站,而後再建立一個課程(Course),把以前上傳的視頻放到該課程下,等全部的課程視頻都審覈經過了,則就能夠發佈這個課程了,學習者就能夠在網上看到該課程。高併發
上面那段話只是對「視頻課程」場景作了大體的描述,下面我將逐條以用例(Use Case)的方式作一個列表:工具
從Use Case中能夠很容易的獲得這幾個概念(名詞):視頻、課程。(由於本次建模只關注課程與視頻的關係,不考慮用戶);業務的行爲概念也很容易獲得:審覈、發佈。性能
業務模型很簡單,課程和視頻是典型的多對多關係。學習
經過場景能夠分析出以下兩個實體:視頻、課程網站
必須知足的業務規則:
一、發佈課程時,要求該課程下的關聯的視頻個數≥3
二、發佈課程時,其下的全部視頻必須都是已經審覈經過的狀態
很顯然,在場景描述時,咱們隱含的表達瞭如下這些概念:
一、視頻能夠單獨上傳,管理,審覈等,從業務規則上來看,它不依賴於任何課程的狀態
二、課程能夠單首創建,在發佈時須要依賴視頻的個數和狀態
因此,視頻顯然是一個聚合根。在現有的業務場景中,課程也是一個聚合根,這個應該沒有歧義。業務規則1和2也是在課程這個聚合下體現,並保證業務規則被知足。
有一個最容易想到的非DDD的設計,代碼可能以下:
class Course { public string Id { get; } public ICollection<Video> Videos { get; } public void Publish() { if (Videos.Count < 3) throw Exception("One course must associate 3 videos."); if (Videos.Any(v => !v.Approved)) throw Exception("All videos need to be approved."); ... } } class Video { public string Id { get; } public bool Approved { get; set; } public ICollection<Course> Courses { get; } }
先不說這個設計的好壞,咱們直接來看從DDD的角度如何設計這兩個聚合根。
首先一點,聚合之間不可直接引用(內存引用),必須以Id的方式引用,由於根據定義聚合是不容許接觸到他們外部的. (若是容許了那意味着聚合再也不是一個事務邊界, 咱們也再也不能充分的推導出聚合有能力確保他的不變性;這也將妨礙到聚合的分片處理)
由於課程發佈時,須要驗證其下所關聯視頻個數和是否所有審覈,因此須要一個視頻信息的實體聚合在課程下。
class Course { public string Id { get; } public ICollection<VideoInfo> VideoInfos { get; } //視頻信息的實體集合 public void Publish() { if (VideoInfos.Count < 3) throw Exception("One course must associate 3 videos."); if (VideoInfos.Any(v => !v.Approved)) throw Exception("All videos need to be approved."); ... } } //視頻信息,至少包含視頻Id和是否已審覈的狀態 class VideoInfo { public string VideoId { get; } public bool VideoApproved { get; set; } } class Video { public string Id { get; } public bool Approved { get; set; } public ICollection<string> Courses { get; } //Course Id的集合 }
爲了保證高併發及性能,聚合之間採用最終一致性,且聚合之間的交互經過Saga來完成。當視頻審覈經過後,視頻聚合根發消息通知全部關聯它的課程,以便更新課程中的VideoInfo的狀態,因此Video聚合根下要有課程Id集合的緣由。
在DDD中,有個例子常常被用來說解聚合、實體、值對象的概念,那就是電商領域的訂單(Order)、訂單項(OrderLine)、商品(Product)。其實上面的場景很相似於Order、OrderLine、Product之間的關係,課程至關於Order,視頻至關於Product,視頻信息至關於OrderLine。這也許就是觸類旁通的體現吧。
注:此圖來源於湯雪華的博客
歡迎你們加入ENode羣討論DDD和牛逼的ENode框架,QQ羣號:185916873。羣主(湯雪華,netfocus)博客:http://www.cnblogs.com/netfocus/