领域驱动设计part2

来源:互联网 发布:seo管家 编辑:程序博客网 时间:2024/06/12 13:54



做完顶层的分析,其实是业务架构分析,接下来就是领域设计,领域设计是最有艺术的部分,也就是提炼具体的领域概念和相关关系。



这里有些概念需要先认识一下。先看“实体”,大家以前做过数据库的建模,建立E-R图,每个表通过ORM工具映射为实体类,也就是PO,领域模型中的实体很类似这个PO,不过PO只有数据没有行为,而领域模型的实体是有行为的,所以有所区别,再就是“值对象”,实体和值对象最大的区别在于实体是通过ID作为标志,而“值对象”只要内容一样,就完全是同一个东西,比如我叫王立,另外一个人也叫王立,但是我们的身份证不一样,不是同一个人,这叫“实体”,但是一个地址,只要内容一样,就是唯一的,这叫做“值对象”,因此值对象不需要id,实体才需要。实体都有生命周期,如果一个实体的销毁,会导致另外一个实体也被销毁掉,他们的生命周期是捆绑在一起的,那就可以被当作一个聚合,聚合根指的是,这个聚合的生命周期的主导者,它死了,其它实体都没有存在的意义,无论是实体还是聚合根,他们都是有行为的,并且,他们也都是有状态的,状态就是属性。还有一种类型的业务模型,叫领域服务,用于协调那些对象与对象之间的交互的,它只有行为没有状态,也就是没有属性,但是,又可以独立于应用场景,被多个场景复用,这是它区别于应用层服务的地方,应用层服务和场景有关,也就是和用例是一一映射的,领域服务不是,它更具有独立性,可以被应用层服务进行装配使用。




这张图是概念关系图:工厂创建领域对象,领域服务用于协调那些聚合根与聚合根之间的交互,接下去就是聚合根,聚合根包含了下面的实体,包含了值对象,领域服务关注的是群体的行为,使得多个聚合根进行交互。而聚合根关注的是个体的行为。这些聚合被持久化的时候,要通过仓储进行存储。这张图,我们把这些分类都以标签的形式标注在业务对象上。




这些业务对象是如何被抽取出来的呢?我们是从业务场景入手的,逐步去理解交互的过程,逐步找出这些概念。再对它们的关系进行梳理。




遵循如下的一些原则:

关联尽量少,对象之间的复杂的关联容易形成对象的关系网,这样对于我们理解和维护单个对象很不利,同时也很难划分对象与对象之间的边界;另外,同时减少关联有助于简化对象之间的遍历;


一对多的关联也许在业务上是很自然的,通常我们会用一个集合来表示1对多的关系。但我们往往也需要考虑到性能问题,尤其是当集合内元素非常多的时候,此时往往需要通过单独查询来获取关联的集合信息;


关联尽量保持单向的关联;

在建立关联时,我们需要深入去挖掘是否存在关联的限制条件,如果存在,那么最好把这个限制条件加到这个关联上;往往这样的限制条件能将关联化繁为简,即可以将多对多简化为1对多,或将1对多简化为1对1;



例如:这个是订单和订单项,他们之间的关系是一对多的关系,等等。接下来,就是去定义聚合根。比如说订单有一些订单项,他们的生命周期是从属的关系,订单没有了,订单项也就没有了。所以订单是聚合根。




这里需要注意的是,聚合不等于大对象,相反聚合要尽量设计得小,后面我们会讲到,我们是以聚合为单位整个地加载对象进行操作的,聚合很大的话,内存消耗很大,会导致性能上的问题,另外逻辑控制也会比较复杂。由于聚合是用来捆绑需要维持一致性状态的对象,而不是简单的将对象捆在一起,所以,如果它们内部没有需要维持一致性的规则,就可以让这个内部实体独立为聚合根,使得聚合的结构尽可能小,还有一点要注意,对于子对象的集合很大的情况,聚合根不应该一次性加载它们,而应该使用查询方法按需加载。


聚合之间的关联是通过ID,而不是对象引用。为什么是这样?大家都知道在我们这些对象拉进内存里面以后,这些对象与对象之间会形成一个网状的结构,如果这个网状结构无限的蔓延,就会造成我们的性能损耗非常的厉害。事实上一些数据或者是对象是可以延迟加载的,我们不需要一次性拿出这么多的对象出来,但是后面会讲到,领域对象不是PO,没办法直接被ORM进行管理和延迟加载,所以,为了确保不会立刻加载,我们采用ID来建立聚合根之间的关联,在需要的时候,通过引用另一个聚合根的仓储去获取它。


聚合根之间传递的是数据副本,而不能直接传递内部对象的引用,以确保一致性封装的有效性。如果内部对象能够被引用到外部去,实际上就已经破坏了聚合根的封装能力,因为外部对象拿到聚合内部的对象地址引用,是可以直接操作该对象的内容的。因此,聚合与聚合之间是通过接口进行交互,彼此之间只能够传递数据的副本,而不是对象的引用。这样使得聚合和聚合之间是隔离的,每个聚合以后甚至可以放在独立的容器中,扩展为微服务,当然,微服务的粒度要适当控制。


由于聚合之间彼此独立,所以聚合内可以要求强一致性,聚合之间只要最终一致性就可以了,这使得聚合的关系可以是分布式的,可以独立进行细粒度的性能扩容,增强了系统的延展能力。


领域驱动的一个重要的原则就是采用信息专家的原则,把数据对象和操作是捆绑在一起的,我们以前采用传统的做法就是会把数据丢给很多的服务去处理。这里面带来一个问题,就是说由于数据和他的业务逻辑是分离的,分离了以后,这个数据会在各种服务的逻辑碎片中被修改,而这些逻辑碎片的一致性是没有一个地方进行统一维护的,这种碎片化的逻辑在事务脚本化的系统中比比皆是,所以,你会发现系统很难维护,尤其是复杂的业务系统,现在,数据一致性相关的逻辑操作都在聚合体内封装和维护的话,那么这个数据就比较健壮了,这实际上是一种保护性的编程。




我们总结一下领域设计的步骤:划分好边界上下文,发现哪些是应用层,哪些是领域层,在每个边界上下文中设计领域模型,核心是找出聚合根,画出领域模型图,划分每个模型的聚合边界。再结合应用层功能,思考领域模型是如何在业务中经的发挥的作用,最后是场景走查和重构。



当我们把领域模型建立起来之后,需要把它隔离到领域层,看这里,这是用户接口层,它只是操作数据的输入输出,不带有任何的业务逻辑,他们之间传递的数据也都是纯粹的数据传输对象。应用层是跟场景有关的,和用例一一对应。服务层是可复用的事务脚本,和场景无关,它是无状态的,负责操作调配领域层对象,领域层封装了和状态有关的领域对象,它是最稳定的核心。大家从学校毕业出来,在学校里你学的知识或能力,无论哪一个岗位上,这些知识能力都不变,变化的只是你使用知识或能力的方式,知识能力的意义就等同于领域层的意义,它封装了稳定的概念与规则,而工作岗位有关的,经常变化的部分等同于用例或服务层,它封装了场景对领域层的装配和使用方式。

原创粉丝点击