DDD-领域对象与领域服务

问题

  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.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();

在这里插入图片描述

发布了168 篇原创文章 · 获赞 333 · 访问量 50万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: Age of Ai 设计师: meimeiellie

分享到微信朋友圈

×

扫一扫,手机浏览