上一篇说的聚合,通过聚合设计可以真实的反应现实世界的状况,提高软件设计的质量,有效降低维护变更的成本。
仓库+工厂
将聚合真正落实到软件设计中,要需要另外两个重要概念:仓库,工厂。
举个例子:
创建一个订单,订单中包含了多个订单明细,并且将他们做成了一个聚合。
这是,当订单完成了创建,就需要保存到数据库中。
需要同时保存订单,订单明细表,并且做到一个事物中。
问题:谁来负责保存数据入库,并对其添加事物呢?
贫血模型步骤如下
- 通过订单dao与订单明细dao去完成db的保存,
- 由订单service添加事物
缺点:没有聚合,缺乏封装,不利于日后维护
聚合设计如下:
订单与订单明细的保存的实现封装到订单仓库中,(DDD的设计通常会实现一个仓库(Repository)来完成对数据库的访问),仓库与dao有啥区别呢?
一般来说dao是对数据库中某一个表的访问,比如:订单有订单dao,明细有明细dao,用户有用户dao,当数据要保存到db时,有dao负责保存,但是保存的是单个表。订单dao保存订单表,明细dao明细表,用户dao保存用户表。
当查询数据时,通过dao查询的也是某一个单表。
问题:如果在查询订单时要显示用户名称,如何处理呢?
-
传统方案:另外定义一个订单对象,并在该对象里增加“用户名称”,通过订单dao查询订单表时,在sql中join用户表就可以达到目的。但是这样会非常别扭的设计出2个,或者多个订单对象,并且新增加的订单对象与领域模型中的订单对象有较大的差别,显得不够直观。
当系统业务逻辑复杂,程序阅读起来困难,变更也会很麻烦。 -
因此在应对复杂业务系统时,我们希望程序设计能较好地与领域模型对应上,领域模型是什么样,程序就设计成什么样。
class Order {
private String orderId;
private String buyerId;
private List<String> orderItemIds;
// get&set
}
在订单对象中加入了订单明细对象与用户对象的引用,订单对象与用户对象是多对一关系,是对于对象的引用。订单对象与订单明细对象是一对多的关系,是对集合对象的引用。
这样,当创建订单对象时,在该对象中填入buyerId与明细集合orderItems,然后交给订单仓库去保存,在保存时就进行了封装,同时保存订单与订单明细2张表,并在其上添加了一个事物。
特别注意:对象间的关系是否是聚合关系,在保存时是有差别的。
订单与订单明细是聚合关系,因此在保存订单时需要保存订单明细,并且放到同一个事物中。
然而,订单与用户不是聚合关系,在保存订单时,不会去操作用户表,只有在查询订单时,才会查询与订单对应的用户。
订单的保存是一个比较复杂的保存过程,但是通过订单仓库的封装,对于客户程序来说,不需要关心是如何保存的。
装载(Load)-- 通过主键ID去查询某一条记录。
比如:要装载一个订单,就是通过一个订单id去查询这个订单。
通过订单仓库装载订单时步骤如下:
- 简单的查询订单表,将其封装在订单对象中。
- 再去查询补填用户对象,与订单明细对象。
- 将用户,订单明细对象装配到订单对象中。
这时,创建,装配的工作都交给了工厂来完成。
DDD中的工厂与设计模式中的工厂不是一个概念,
设计模式中,将被调用方设计成一个接口下的多个实现,将这些实现放入工厂中,工厂负责通过key找到对应的实现类,创建出来,返回给调用方,从而降低了调用方与被调用方的耦合度。
DDD中的工厂与设计模式中的工厂唯一的共同点,他们都要去做创建对象的工作
DDD中的工厂通过装配创建领域对象,是领域对象生命周期的起点,
比如:系统要通过ID装载一个订单
- 订单仓库将任务交给订单工厂,订单工厂分别调用订单dao,订单明细dao,用户dao(接口)进行查询
- 将订单明细对象与用户对象,分别set到订单对象的“订单明细”与“用户”属性中
- 订单工厂将装配好的订单对象返回给订单仓库
- 以上是DDD中工厂的工作内容
DDD中的仓库
然而,当订单工厂将订单对象返回给订单仓库后,订单仓库并非简单的将该订单对象返回到客户程序,还需要缓存领域对象。
DDD中仓库的概念是:如果服务器非常强大,那么我们就不需要数据库了,系统创建的任何领域对象都可以放在仓库中,当需要这些对象时通过id去仓库中获取,但是现实世界中没有如此强大的服务器做仓库,因此会将领域对象持久化到数据库中。
数据库是仓库进行数据持久化的一种内部实现。也可以有另一种内部实现,就是将最近反复使用的领域对象放进缓存里。这样,当客户程序通过id去获取某个领域对象时,仓库会通过这个id先到缓存里查找。1. 查找到就会返回,2. 没查找到则会通知工厂,工厂调用dao去数据库查找,然后装配成领域对象返回给仓库,仓库返回数据到客户程序的过程中,可以选择将数据缓存起来。
通过查询条件装载订单
- 订单仓库通过订单dao查询订单表,不join
- 订单dao查询订单表后,并且分页,将某一页的数据返回给订单仓库。
- 订单仓库将查询结果交给订单工厂,去补填对应的用户与订单明细,完成相应的装配,将装配好的订单对象集合返回给仓库。
通过仓库与工厂,对原有的dao进行了一层封装,在保存,装载,查询等操作中,加入聚合,装配等操作,并将这些操作封装起来,对上层的客户程序屏蔽。
这样客户程序就不需要关注 以上操作,从而降低 技术门槛。并且变更与维护也简单了。
通过聚合实现了整体与部分的关系
客户程序只能操作整体,而将对部分的操作封装在了仓库与工厂中。
网友评论