为什么把这么小的点拿出来讲,最开始在讨论中领域对象与领域服务时,觉得行为放在service/entity中区别不大,只是一个放置位置的问题,并不影响到代码的抽象和复用,所以没有实行。但是最近在推动产品进行DDD业务建模,发现这个问题非常重要,关系到代码是否清晰表达了业务,这个也是我们进行DDD的初衷。
领域对象: 聚合根,实体,值对象 领域的数据与行为, 数据和行为应该与业务产品上的行为关联。领域对象通常是有状态的,理想情况下,我们的领域对象行为应该和产品业务定义意义映射
观点
首先需要对概念明确定义,因为DDD其实是做了一个问题的分治,所以必然会导致在某些情况下,会有单薄这个说法。就像垂直架构中dao/manager/service层区分一样。在初期我们可以明确按照概念来放置代码,当大家达成共识,深刻理解了这些概念时,没有其中一层也无所谓了。
举个例子 eg. 一个bad case 三个模型:A,B,C,他们之间存在状态变更流动。
整理出来的状态变更图
AService.updateXXStatus
AService.cancelBy
AService.changeStatus()
这些方法都在处理状态,反应不了业务的情况
一般包含以下逻辑
publicRoulettePrize executeRoulette(finalList<RoulettePrize> prizes,
finalInteger dailyFreeRouletteCount,
finalint rouletteCountToday,finalUserDTO userDTO)throwsBizzException{
finalList<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());
finalint totalRate = validPrize.stream().mapToInt(p -> p.getRate()).sum();
// 排除无效奖品,计算有效奖品概率之和
if(CollectionUtils.isEmpty(validPrize)){
throwRouletteResponseCode.convertBizzException(RouletteResponseCode.PRIZE_EMPTY);
}
return chooseResultPrize(validPrize, totalRate ==0?1: totalRate);
}
不应该做的事
领域对象不应该与其他的模型有交互,如manager(资源层管理),不应该持久化数据 如何持久化不应该是领域对象关心的。
领域服务通常是领域对象的调用方,是微服务架构下,领域对象对外提供的方式。
AService
// 构建领域对象
final List<AAggr> aggr = mAManager.listByUserIds(userVal);
final AEntity entity = CollectionUtils.isEmpty(aggr) ? null : aggr.get(0)
.getA();
// 调用领域对象方法
entity.checkDesignerExist();
entity.checkUpdate();