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/

相关文章
相关标签/搜索