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)的方式做一个列表:
- 用户可以把一个或多个视频上传到网站,并等待审核
- 用户创建了一个课程,比如叫《Lisp从入门到放弃》
- 用户把一个或多个视频关联到该课程下(关联的时候不要求视频是已审核状态)
- 用户发布创建的课程时,要求其下至少关联3个以上的视频,且这些视频必须是已审核通过。只有满足了这两个要求才能发布课程
- 一个视频可以放到多个课程下
从Use Case中可以很容易的得到这几个概念(名词):视频、课程。(因为本次建模只关注课程与视频的关系,不考虑用户);业务的行为概念也很容易得到:审核、发布。
业务模型很简单,课程和视频是典型的多对多关系。
分析
通过场景可以分析出如下两个实体:视频、课程
必须满足的业务规则:
1、发布课程时,要求该课程下的关联的视频个数≥3
2、发布课程时,其下的所有视频必须都是已经审核通过的状态
很显然,在场景描述时,我们隐含的表达了以下这些概念:
1、视频可以单独上传,管理,审核等,从业务规则上来看,它不依赖于任何课程的状态
2、课程可以单独创建,在发布时需要依赖视频的个数和状态
所以,视频显然是一个聚合根。在现有的业务场景中,课程也是一个聚合根,这个应该没有歧义。业务规则1和2也是在课程这个聚合下体现,并保证业务规则被满足。
设计
有一个最容易想到的非DDD的设计,代码可能如下:
class Course { public string Id { get; } public ICollection<Video> Videos { get; } public void Publish() { ) 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() { ) 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/
DDD建模案例----“视频课程”场景的更多相关文章
- 《Python从菜鸟到高手》已经出版,开始连载了,购买送视频课程
好消息,<Python从菜鸟到高手>已经出版!!! JetBrains官方推荐图书!JetBrains官大中华区市场部经理赵磊作序!送2400分钟同步视频课程!500个案例,400道P ...
- Spring Boot 实战与原理分析视频课程
Spring Boot 实战与原理分析视频课程 链接:https://pan.baidu.com/share/init?surl=PeykcoeqZtd1d9lN9V_F-A 提取码: 关注公众号[G ...
- IT视频课程集
马哥Linux培训视频课程:http://pan.baidu.com/s/1pJwk7dp Oracle.大数据系列课程:http://pan.baidu.com/s/1bnng3yZ 天善智能BI培 ...
- IT视频课程集(包含各类Oracle、DB2、Linux、Mysql、Nosql、Hadoop、BI、云计算、编程开发、网络、大数据、虚拟化
马哥Linux培训视频课程:http://pan.baidu.com/s/1pJwk7dp Oracle.大数据系列课程:http://pan.baidu.com/s/1bnng3yZ 天善智能BI培 ...
- 传智 Python基础班+就业班+课件 【最新完整无加密视频课程】
点击了解更多Python课程>>> 传智 Python基础班+就业班+课件 [最新完整无加密视频课程] 直接课程目录 python基础 linux操作系统基础) 1-Linux以及命 ...
- 《Python爬虫技术:深入理解原理、技术与开发》已经出版,送Python基础视频课程
好消息,<Python爬虫技术:深入理解原理.技术与开发>已经出版!!! JetBrains官方推荐图书!JetBrains官大中华区市场部经理赵磊作序!送Python基础视频课程!J ...
- 视频课程 | Kubernetes的兴起
视频课程 | Kubernetes的兴起 原创: 京小云 京东云开发者社区 4月3日 京东云开发者社区在3月底于北京举行了以"Cloud Native时代的应用之路与开源创新"为 ...
- 花了半年时间,我把Pink老师的HTMLCSS视频课程,整理成了10万字的Markdown笔记!
说明:本文内容真实!!!不是推广!!! 学习前端的同学应该都或多或少听说过 Pink 老师,我个人觉得 Pink 老师的前端视频教程应该说是目前B站上最好的了,没有之一! Pink老师 HTML CS ...
- QT5 QSS QML界面美化视频课程系列 QT原理 项目实战 C++1X STL
QT5 QSS QML界面美化视频课程系列 QT原理 项目实战 C++1X STL 课程1 C语言程序设计高级实用速成课程 基础+进阶+自学 课程2 C语言程序设计Windows GDI图形绘 ...
随机推荐
- java ---- 面试题
1.java 语言如何进行异常处理,关键字:throws.throw.try.catch.finally分别代表什么意义?finally代码是在return之后还是之前执行? throws是获取异常, ...
- coreseek安装过程
一.sphinx 全文检索 通过sphinx检索到id,然后到mysql里面拿到记录 什么是劝我呢检索?结构化数据: 具有固定格式或者长度的数据非结构化数据: 标题 内容 等不定长的数据非机构化数据还 ...
- .vue文件里引用单独样式和js文件
style只能引一个,script可以引多个
- 1.2、Workspace中让Package分层显示
有时候我们新建两个具有两个相同目录的Package(例如:com.st.collection和com.st.map这两个Package)时,在Workspace中是以平铺的方式显示的,如: 当Pack ...
- [Tomcat] Tomcat的classloader
定义 同其他服务器应用一样,tomcat安装了各种classloader(classes that implement java.lang.ClassLoader) Bootstrap | Syste ...
- Android 项目结构图
src:存放Java源代码 gen:存放系统自动生成的配置文件 Android 4.4.2:包含Android.jar文件,包含构建应用程序所需的所有Android SDK库 asssets:存放资源 ...
- scanf_s
很多带“_s”后缀的函数是为了让原版函数更安全,传入一个和参数有关的大小值,避免引用到不存在的元素,有时hacker可以利用原版的不安全性黑掉系统 例如: ANSI C中没有scanf_s(),只有s ...
- Buddy内存分配算法
Buddy(伙伴的定义): 这里给出伙伴的概念,满足以下三个条件的称为伙伴:1)两个块大小相同:2)两个块地址连续:3)两个块必须是同一个大块中分离出来的: Buddy算法的优缺点: 1)尽管伙伴内存 ...
- 执行ssh-add时出现Could not open a connection to your authentication agent
若执行ssh-add /path/to/xxx.pem是出现这个错误:Could not open a connection to your authentication agent,则先执行如下命令 ...
- 在Web Api中集成protobuf
安装WebApiContrib.Formatting.ProtoBuf Install-Package WebApiContrib.Formatting.ProtoBuf 注册ProtoBufForm ...