DDD实战课
欧创新
人保高级架构师
立即订阅
4865 人已学习
课程目录
已完结 23 讲
0/2登录后,你可以任选2讲全文学习。
开篇词 (1讲)
开篇词 | 学好了DDD,你能做什么?
免费
基础篇 (5讲)
01 | 领域驱动设计:微服务设计为什么要选择DDD?
02 | 领域、子域、核心域、通用域和支撑域:傻傻分不清?
03 | 限界上下文:定义领域边界的利器
04 | 实体和值对象:从领域模型的基础单元看系统设计
05 | 聚合和聚合根:怎样设计聚合?
进阶篇 (6讲)
06 | 领域事件:解耦微服务的关键
07 | DDD分层架构:有效降低层与层之间的依赖
08 | 微服务架构模型:几种常见模型的对比和分析
09 | 中台:数字转型后到底应该共享什么?
10 | DDD、中台和微服务:它们是如何协作的?
答疑:有关3个典型问题的讲解
实战篇 (10讲)
11 | DDD实践:如何用DDD重构中台业务模型?
12 | 领域建模:如何用事件风暴构建领域模型?
13 | 代码模型(上):如何使用DDD设计微服务代码模型?
14 | 代码模型(下):如何保证领域模型与代码模型的一致性?
15 | 边界:微服务的各种边界在架构演进中的作用?
16 | 视图:如何实现服务和数据在微服务各层的协作?
17 | 从后端到前端:微服务后,前端如何设计?
18 | 知识点串讲:基于DDD的微服务设计实例
19 | 总结(一):微服务设计和拆分要坚持哪些原则?
20 | 总结(二):分布式架构关键设计10问
结束语 (1讲)
结束语 | 所谓高手,就是跨过坑和大海!
DDD实战课
登录|注册

14 | 代码模型(下):如何保证领域模型与代码模型的一致性?

欧创新 2019-11-15
你好,我是欧创新。
[第 12 讲] 中,我们了解了如何用事件风暴来构建领域模型,在构建领域模型的过程中,我们会提取出很多的领域对象,比如聚合、实体、命令和领域事件等。到了 [第 13 讲],我们又根据 DDD 分层架构模型,建立了标准的微服务代码模型,为代码对象定义好了分层和目录结构。
那要想完成微服务的设计和落地,这之后其实还有一步,也就是我们今天的重点——将领域对象映射到微服务代码模型中。那为什么这一步如此重要呢?
DDD 强调先构建领域模型然后设计微服务,以保证领域模型和微服务的一体性,因此我们不能脱离领域模型来谈微服务的设计和落地。但在构建领域模型时,我们往往是站在业务视角的,并且有些领域对象还带着业务语言。我们还需要将领域模型作为微服务设计的输入,对领域对象进行设计和转换,让领域对象与代码对象建立映射关系。
接下来我们围绕今天的重点,详细来讲一讲。

领域对象的整理

完成微服务拆分后,领域模型的边界和领域对象就基本确定了。
我们第一个重要的工作就是,整理事件风暴过程中产生的各个领域对象,比如:聚合、实体、命令和领域事件等内容,将这些领域对象和业务行为记录到下面的表格中。
你可以看到,这张表格里包含了:领域模型、聚合、领域对象和领域类型四个维度。一个领域模型会包含多个聚合,一个聚合包含多个领域对象,每个领域对象都有自己的领域类型。领域类型主要标识领域对象的属性,比如:聚合根、实体、命令和领域事件等类型。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《DDD实战课》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(29)

  • ANYI
    1,对于实体采用充血模型,包含自己的属性及行为,例如保持、更新、删除等行为方法,需要持久化,依赖基础层数据库操作,是在实体直接引入,例如mybatis的mapper?
    2,对于相对简单的实体操作增删改查这种,需要暴露到接口层;那要一层一层向上封装,实体》领域服务》应用服务》接口服务;这样是不是又显得代码很多余;一个简单的增加修改方法接口,需要很多冗余代码,上层也没有其他逻辑,封装一下调用下层,写一个接口,要写很多层次调用,是否会很臃肿啰嗦,是不是就可以直接接口层封装就省去一些层呢?
    3,在服务编排上有没有一些框架什么的?还是都是通过if else的手写?

    作者回复: 1、实体的这些数据库映射是通过mapper来实现的。
    2、松散分层架构是可以跨层调用了,实现起来很容易。但是在复杂的情况下,服务不太容易管理,比如,你可能不知道你的方法到底被谁组合和封装了,一旦出现方法变更,你不容易一次找出所有受影响方。而逐层封装的话,你只需要逐层通知到上层就可以了。
    3、微服务内的服务编排相对简单,就是业务逻辑的执行顺序而已,个人感觉不需要引入什么工具。

    2019-11-15
    5
    2
  • 何沛
    abcAppService{
          abDomainService();
          c.f();//老师这里是不是应该调整为cDomainService();
    }

    作者回复: 谢谢指出,图已调整。

    2019-11-15
    1
  • 张迪
    领域实体的方法必须要通过领域服务封装一下吗?

    作者回复: 如果直接对外暴露的话,建议封装一下。避免业务逻辑泄露。

    2019-11-15
    1
  • 盲僧
    作者代码的地址可以发一下吗,或者给个demo

    作者回复: 等我有时间的时候,我整理一个吧。

    2019-11-15
    1
  • 鸭子
    领域服务过多会不会导致领域中的实体对象对象变成贫血模型,如果没有领域服务又很可能把领域内的逻辑放到应用服务用去,纠结。

    作者回复: 实体对象的方法在实体类内实现,有些领域服务只是做封装或者组合编排。
    领域逻辑别放到应用服务中去,容易让领域模型逻辑与编排逻辑出现混乱,最后做成三层架构了。

    2019-11-15
    1
  • @倾杯
    老师,代码模型这块我还有一点不太确定,就是dto->do这个转换方法具体是在controller中调用还是在xxAppService中调用呢?反过来do->dto方法又该是在哪个类里调用呢?

    作者回复: 你可以在用户接口层创建DTO类和assembler类。在assembler类里完成映射。

    2019-12-09
  • 鱼养猫
    服务A调用服务B,是放在基础设施层吗?

    作者回复: 你说的是微服务之间的服务调用,还是微服务内的应用服务或领域服务?微服务之间服务调用可以通过API网关来调用,API网关是基础层的组件。微服务内的服务调用,直接服务跨层调用就可以了。

    2019-12-06
  • 鱼养猫
    数据的持久化是在领域服务中还是实体中?如果持久化放在实体中,使用spring和mybatis就得把实体声明@Component 注入mapper,还得设置多例.持久化是否可以放在领域服务中去做?

    作者回复: 持久化是通过仓储来做。这样通过依赖倒置,可以解耦领域模型实现与数据库资源。

    2019-12-06
  • Geek_4660f3
    1、领域服务和聚合根是什么关系
    2、领域服务是封装了多个实体的业务,那么领域服务是存放在聚合根内的么
    3、应用服务是直接调用领域服务还是调用聚合根

    作者回复: 聚合根由于管理了聚合内所有的实体和值对象,它的有些方法其实可以替代部分领域服务的功能。但是我建议聚合根本身的业务行为放在聚合根的方法内,聚合内的所有领域服务用一个单独的领域服务类来实现。
    应用服务应该调用领域服务。

    2019-12-01
  • Jesen
    老师,如果把仓储Respository的实现放到基础设施层,其仓储接口定义在领域层里面,那么在领域层里面该怎么持久化呢,可以通过在应用层中将仓储注入到领域服务里面来实现吗?

    作者回复: 这个只是代码存放目录的考虑。具体的持久化是在仓储实现的服务里面。为了方便聚合的重新组合,我在代码目录结构里面将仓储的接口和实现都放在领域层的聚合目录下,如果微服务架构演进,你可以直接将聚合相关的代码一起拿走。

    2019-11-29
  • Marshall
    “如果一个业务动作或行为跨多个实体,我们就需要设计领域服务”这句怎么理解?
    这多个实体是聚合内的,还是说跨聚合的?如果是聚合内的,不是由聚合中的行为来处理吗?如果是跨聚合的,但是实体是通过聚合根访问的,是不是这句话要改为“如果一个业务动作或行为跨多个聚合,就需要设计成领域服务”

    作者回复: 不是。
    实体完成自己的业务行为,但是聚合内有一些业务逻辑是由多个实体共同完成的。这种业务行为是跨了多个实体。比如支付订单的操作,需要对订单以及订单明细两个实体进行操作,这个行为就是一个领域服务。

    2019-11-28
  • zj
    CreatePersonInfo这个方法,如果传入的参数太多怎么办,代码会不会不太优雅

    作者回复: 参数大部分都是实体对象。

    2019-11-22
    3
  • zj
    生成客户编码这个有个问题,如果是要用到中间件生成客户编码,那这个聚合根Person不就依赖了基础层的中间件了吗

    作者回复: 你这通过生成客户编码的领域服务,调用中间件的服务就可以吧。或者在领域服务内按照业务规则自己生成编码也可以吧。

    2019-11-22
    2
  • Geek_1c00cb
    前面一直在讲仓储层的依赖反转,代码实现时怎么又放到领域里面了,是领域里面控制如何选择仓储,而不是应用层?

    作者回复: 这是为了聚合之间代码重组的方便,一次可以将聚合代码和仓储代码迁移走。上一节讲聚合的代码目录的时候有特别说明的。

    2019-11-19
    1
  • 杨杰
    关于接口层、应用层、领域层在相互调用的时候应该采用什么形式传输呢?比如说接口层接收的参数肯定是DTO对象,那么DTO是否可以传入到下面的应用层甚至领域层呢?还是应用层和领域层都需要定义自己的传输对象?

    作者回复: 有dto,do,po等对象,不同的层采用不同的对象。第16节会有专门介绍。

    2019-11-19
    1
  • 杨杰
    在实际操作的时候经常会碰到一个问题,加入一个领域服务内部需要创建两个聚合(或实体)完成业务操作,有几个细节的问题:
    1、在创建这个实体的问题,是在领域服务里面把这个两个实体的大部分属性直接赋值,还是应该有工厂方法来创建实体?
    2、关于实体封装核心业务逻辑,在这个例子里面怎么才能算核心业务逻辑呢?比如说通过单件*数量来计算金额?

    作者回复: 1.通过聚合根调仓储来做数据初始化。复杂的用工厂,不复杂的没必要用工厂。
    2计算的业务逻辑一般都是核心逻辑。

    2019-11-18
    2
  • 阿玛铭
    项目正好做到这一步,记录一下:
    应用服务对领域服务的编排和封装技巧。
    关注应用服务的简洁性和实用性。服务api的编排有大而全、实用而精简(来自用户界面、定时任务、领域事件等触发源头的需求)两种。如果希望较低的层次更加稳定,就需要基础层(非四层模型内容)微服务提供大而全的api集合,再由较高层的微服务根据触发源的需求做编排。这里涉及业务操作数据的一致性和高层微服务的灵活性之间的权衡。大而全需要更多地引入事件总线(MQ)或者分布式事务中间件,实用而简洁会导致前台灵活性不足,进而影响平台级别的复用能力。
    2019-11-17
  • 陈云
    领域类,有领域异常吧?比如我有一个背包聚合,扣减背包库存的时候,可能抛出库存不够的异常,这种异常也是要定义在领域层吧?

    作者回复: 是的。

    2019-11-17
  • Todd BD
    如果我有两个微服务, 查询场景下, 服务A需要调用服务B去获取服务B中聚合的一个值对象(Z.class), 对于在服务A中,从服务B里拿来的这个值对象也同样是当值对象使用并绑定在服务A的一个聚合对应的DTO里, 请问服务A是否能直接使用服务B包中的Z.class,还是说需要在A服务中重新创建一个相应的Class以起到防腐层的效果?

    作者回复: 都两个微服务了,在两个不同的项目里,要重建一个新的值对象的class的。

    2019-11-16
    3
  • Geek_2973a9
    除了聚合根管理功能外,我们还可以用 DDD 的其它设计方法——请问还有哪些方法?

    作者回复: 分层啦,依赖关系,领域划分,事件风暴等等,太多了。

    2019-11-16
收起评论
29
返回
顶部