zoukankan      html  css  js  c++  java
  • DDD建模案例----“视频课程”场景

      接触领域驱动设计DDD有一年多的时间了,中间看过不少书,参与过一些讨论(ENode QQ群)。目前对DDD的认知还停留在理论阶段,所以对领域建模非常感兴趣,这里说的建模是指以DDD的思想为指导再加上DDD的工具,比如聚合、实体、值对象等等。

      昨天有群友分享了一个建模的案例,我想在这儿记录下来,有两个目的:1、为自己学习DDD储存素材  2、锻炼自己写文章的能力

      关于DDD中的一些概念,请见如下文章链接:

      http://www.cnblogs.com/ivanzheng/p/5533162.html(Ivan翻译)

      http://www.cqrs.nu/Faq(这是原文链接,英文好的可以直接读这个)

     

    场景


      大家肯定都听说过视频课程网站吧,比如慕课网。这儿要说的场景跟这个网站的后台管理比较相关。比如,课程的作者可以上传一些课程视频(Video)到该网站,然后再创建一个课程(Course),把之前上传的视频放到该课程下,等所有的课程视频都审核通过了,则就可以发布这个课程了,学习者就可以在网上看到该课程。

      上面那段话只是对“视频课程”场景做了大致的描述,下面我将逐条以用例(Use Case)的方式做一个列表:

    1. 用户可以把一个或多个视频上传到网站,并等待审核
    2. 用户创建了一个课程,比如叫《Lisp从入门到放弃》
    3. 用户把一个或多个视频关联到该课程下(关联的时候不要求视频是已审核状态)
    4. 用户发布创建的课程时,要求其下至少关联3个以上的视频,且这些视频必须是已审核通过。只有满足了这两个要求才能发布课程
    5. 一个视频可以放到多个课程下

      从Use Case中可以很容易的得到这几个概念(名词):视频、课程。(因为本次建模只关注课程与视频的关系,不考虑用户);业务的行为概念也很容易得到:审核、发布。

      业务模型很简单,课程和视频是典型的多对多关系。

    分析


      通过场景可以分析出如下两个实体:视频、课程

      必须满足的业务规则:

          1、发布课程时,要求该课程下的关联的视频个数≥3

          2、发布课程时,其下的所有视频必须都是已经审核通过的状态

      很显然,在场景描述时,我们隐含的表达了以下这些概念:

          1、视频可以单独上传,管理,审核等,从业务规则上来看,它不依赖于任何课程的状态

          2、课程可以单独创建,在发布时需要依赖视频的个数和状态

      所以,视频显然是一个聚合根。在现有的业务场景中,课程也是一个聚合根,这个应该没有歧义。业务规则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/

  • 相关阅读:
    又来项目了,星座运势widget
    ubuntu下编译android源代码
    Android UI,界面辅助设置工具,可随意拖动控件,比google官方提供的方便
    Android 1.5原生软件开发SDK公布
    REST转自WIKI
    Android SDK 1.5 包装索引
    android google market FreshFace上线了,大家都试用试用,反正免费的
    JSON
    ubuntu命令
    网站支付宝接口错误代码:TRADE_DATA_MATCH_ERROR怎么处理? uz
  • 原文地址:https://www.cnblogs.com/junejs/p/5894113.html
Copyright © 2011-2022 走看看