DDD建模案例----「視頻課程」場景

  接觸領域驅動設計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)的方式作一個列表:工具

  1. 用戶能夠把一個或多個視頻上傳到網站,並等待審覈
  2. 用戶建立了一個課程,好比叫《Lisp從入門到放棄》
  3. 用戶把一個或多個視頻關聯到該課程下(關聯的時候不要求視頻是已審覈狀態)
  4. 用戶發佈建立的課程時,要求其下至少關聯3個以上的視頻,且這些視頻必須是已審覈經過。只有知足了這兩個要求才能發佈課程
  5. 一個視頻能夠放到多個課程下

  從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/

相關文章
相關標籤/搜索