前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何避免写出烂的业务代码(2)-领域对象与领域服务

如何避免写出烂的业务代码(2)-领域对象与领域服务

作者头像
方丈的寺院
发布2019-08-05 17:32:16
6140
发布2019-08-05 17:32:16
举报
文章被收录于专栏:方丈的寺院方丈的寺院

问题

  1. 什么是领域对象
  2. 什么是领域服务
  3. 领域对象的行为,与领域服务的行为区别

原因

为什么把这么小的点拿出来讲,最开始在讨论中领域对象与领域服务时,觉得行为放在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.DESIGNERNOTEXIST); } }
  • 领域行为与事件 // 如商品对象的删除,以及事件的publish(不限于CRUD) // 抽奖业务中从奖池中选取奖品
    1. publicRoulettePrize executeRoulette(finalList<RoulettePrize> prizes,
    2. finalInteger dailyFreeRouletteCount,
    3. finalint rouletteCountToday,finalUserDTO userDTO)throwsBizzException{
    4. finalList<RoulettePrize> validPrize = prizes.stream().filter(p -> checkPrizeValid(p))
    5. // 排除掉中奖概率不合法和概率为0的奖品
    6. .filter(p ->ArithUtil.checkIntegerRange(p.getRate(),0,Integer.MAX_VALUE))
    7. // 校验中奖次数限制
    8. .filter(p -> checkPrizeLimit(p))
    9. // 校验vip奖品限制
    10. .filter(p -> checkPrizeVip(p, userDTO))
    11. .filter(p -> checkFreePrize(p,dailyFreeRouletteCount, rouletteCountToday)).collect(
    12. Collectors.toList());
    13. finalint totalRate = validPrize.stream().mapToInt(p -> p.getRate()).sum();
    14. // 排除无效奖品,计算有效奖品概率之和
    15. if(CollectionUtils.isEmpty(validPrize)){
    16. throwRouletteResponseCode.convertBizzException(RouletteResponseCode.PRIZE_EMPTY);
    17. }
    18. return chooseResultPrize(validPrize, totalRate ==0?1: totalRate);
    19. }
  • 状态的流转

不应该做的事

领域对象不应该与其他的模型有交互,如manager(资源层管理),不应该持久化数据 如何持久化不应该是领域对象关心的。

领域服务

  • 构造(复杂的)领域对象 调用防腐层方法,做支撑域和通用域对象的转换与组合
  • 与dao层打交道
  • 调用其他限界上下文的内容
  • 提供领域方法给其他限界上下文/应用程序调用

领域服务与领域对象的关系

领域服务通常是领域对象的调用方,是微服务架构下,领域对象对外提供的方式。

AService

代码语言:javascript
复制
// 构建领域对象
    final List<AAggr> aggr = mAManager.listByUserIds(userVal);
    final AEntity entity = CollectionUtils.isEmpty(aggr) ? null : aggr.get(0)
            .getA();    
   // 调用领域对象方法
    entity.checkDesignerExist();
    entity.checkUpdate();
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-02-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 方丈的寺院 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 问题
  • 原因
  • 定义
  • 几个阻抗
    • 领域对象:
      • 领域服务
        • 领域服务与领域对象的关系
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档