问题
- 什么是领域对象
- 什么是领域服务
- 领域对象的行为,与领域服务的行为区别
原因
为什么把这么小的点拿出来讲,最开始在讨论中领域对象与领域服务时,觉得行为放在service/entity中区别不大,只是一个放置位置的问题,并不影响到代码的抽象和复用,所以没有实行。但是最近在推动产品进行DDD业务建模,发现这个问题非常重要,关系到代码是否清晰表达了业务,这个也是我们进行DDD的初衷。
定义
领域对象:
聚合根,实体,值对象
领域的数据与行为,
数据和行为应该与业务产品上的行为关联。领域对象通常是有状态的,理想情况下,我们的领域对象行为应该和产品业务定义意义映射
几个阻抗
- 觉得行为放在领域服务还是领域对象中区别不大,只是一个放置位置的问题,并不影响到代码的抽象和复用
- 领域对象中还是只有属性,和对象之间的转换
- 业务逻辑没有与代码映射
- manager(持久化操作)放在领域对象中需要进行一个转换(ApplicationContext)或者其他方式
- 我们的业务很单薄,放在领域对象中的内容后,领域服务就很单薄了。滥用了领域服务导致了领域对象的贫血
- 领域对象的集合操作
观点
首先需要对概念明确定义,因为DDD其实是做了一个问题的分治,所以必然会导致在某些情况下,会有单薄这个说法。就像垂直架构中dao/manager/service层区分一样。在初期我们可以明确按照概念来放置代码,当大家达成共识,深刻理解了这些概念时,没有其中一层也无所谓了。
举个例子
eg. 一个bad case
三个模型:A,B,C,他们之间存在状态变更流动。
整理出来的状态变更图
AService.updateXXStatus
AService.cancelBy
AService.changeStatus()
这些方法都在处理状态,反应不了业务的情况
领域对象:
一般包含以下逻辑
-
领域对象的限制
// 如什么样的设计师是不存在的,对于领域外的内容不关心你是不是软删,还是硬删
public void checkDesignerExist() throws BizzException { // 清退的也是不存在的 if (this == null || this.getStatus().equals(DesignerStatusEnum.DELETE)) { throw DdgCoreResponseCode.convertBizzException(DdgCoreResponseCode.DESIGNER_NOT_EXIST); } }
-
领域行为与事件
// 如商品对象的删除,以及事件的publish(不限于CRUD)
// 抽奖业务中从奖池中选取奖品public RoulettePrize executeRoulette(final List<RoulettePrize> prizes, final Integer dailyFreeRouletteCount, final int rouletteCountToday, final UserDTO userDTO) throws BizzException { final List<RoulettePrize> validPrize = prizes.stream().filter(p -> checkPrizeValid(p)) // 排除掉中奖概率不合法和概率为0的奖品 .filter(p -> ArithUtil.checkIntegerRange(p.getRate(), 0, Integer.MAX_VALUE)) // 校验中奖次数限制 .filter(p -> checkPrizeLimit(p)) // 校验vip奖品限制 .filter(p -> checkPrizeVip(p, userDTO)) .filter(p -> checkFreePrize(p,dailyFreeRouletteCount, rouletteCountToday)).collect( Collectors.toList()); final int totalRate = validPrize.stream().mapToInt(p -> p.getRate()).sum(); // 排除无效奖品,计算有效奖品概率之和 if (CollectionUtils.isEmpty(validPrize)) { throw RouletteResponseCode.convertBizzException(RouletteResponseCode.PRIZE_EMPTY); } return chooseResultPrize(validPrize, totalRate == 0 ? 1 : totalRate); }
-
状态的流转
不应该做的事
领域对象不应该与其他的模型有交互,如manager(资源层管理),不应该持久化数据
如何持久化不应该是领域对象关心的。
领域服务
-
构造(复杂的)领域对象
调用防腐层方法,做支撑域和通用域对象的转换与组合 -
与dao层打交道
-
调用其他限界上下文的内容
-
提供领域方法给其他限界上下文/应用程序调用
领域服务与领域对象的关系
领域服务通常是领域对象的调用方,是微服务架构下,领域对象对外提供的方式。
AService
// 构建领域对象
final List<AAggr> aggr = mAManager.listByUserIds(userVal);
final AEntity entity = CollectionUtils.isEmpty(aggr) ? null : aggr.get(0)
.getA();
// 调用领域对象方法
entity.checkDesignerExist();
entity.checkUpdate();