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实战课
登录|注册

13 | 代码模型(上):如何使用DDD设计微服务代码模型?

欧创新 2019-11-13
你好,我是欧创新。
上一讲我们完成了领域模型的设计,接下来我们就要开始微服务的设计和落地了。那微服务落地时首先要确定的就是微服务的代码结构,也就是我今天要讲的微服务代码模型。
只有建立了标准的微服务代码模型和代码规范后,我们才可以将领域对象所对应的代码对象放在合适的软件包的目录结构中。标准的代码模型可以让项目团队成员更好地理解代码,根据代码规范实现团队协作;还可以让微服务各层的逻辑互不干扰、分工协作、各据其位、各司其职,避免不必要的代码混淆。另外,标准的代码模型还可以让你在微服务架构演进时,轻松完成代码重构。
那在 DDD 里,微服务的代码结构长什么样子呢?我们又是依据什么来建立微服务代码模型?这就是我们今天重点要解决的两个问题。

DDD 分层架构与微服务代码模型

我们参考 DDD 分层架构模型来设计微服务代码模型。没错!微服务代码模型就是依据 DDD 分层架构模型设计出来的。那为什么是 DDD 分层架构模型呢?
我们先简单回顾一下 [第 07 讲] 介绍过的 DDD 分层架构模型。它包括用户接口层、应用层、领域层和基础层,分层架构各层的职责边界非常清晰,又能有条不紊地分层协作。
用户接口层:面向前端提供服务适配,面向资源层提供资源适配。这一层聚集了接口适配相关的功能。
应用层职责:实现服务组合和编排,适应业务流程快速变化的需求。这一层聚集了应用服务和事件相关的功能。
领域层:实现领域的核心业务逻辑。这一层聚集了领域模型的聚合、聚合根、实体、值对象、领域服务和事件等领域对象,以及它们组合所形成的业务能力。
基础层:贯穿所有层,为各层提供基础资源服务。这一层聚集了各种底层资源相关的服务和能力。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《DDD实战课》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(37)

  • 陈 争
    不知道我这样理解的对不对
    比如执行一个创建用户的命令,
    1.用户接口层:
      1.1)Assembler->将CustomerDTO转换为CustomerEntity
      1.2)Dto->接收请求传入的数据CustomerDTO
      1.3)Facade->调用应用层创建用户方法
    2.应用层
      2.1)Event->发布用户创建事件给其它微服务
      2.2)Service:
        内部服务->创建用户
        外部服务->创建日志
    3. 领域层
      3.1)Aggregate->进入用户聚合目录下(如:CustomerAggregate)
      3.2)Entity->用户聚合跟
      3.3)Event->创建用户事件
      3.4)Service->具体的创建用户逻辑,比如用户是否重复校验,分配初始密码等
      3.5)Repository->将用户信息保存到数据库

    作者回复: 是的,就是这样的。理解的很到位。

    2019-11-13
    2
    9
  • xj_zh
    老师,求DDD的系统样例代码。

    作者回复: 代码样例还没准备好,后面我找时间整理一下吧。

    2019-11-18
    4
  • zj
    请教一个问题,考虑这样一个场景,主播账户作为一个聚合,优惠券模块作为一个聚合。那主播选券这个命令是不是应该属于主播账户这个聚合里。然后主播账户里的优惠券是不是就是这个聚合里的值对象了

    作者回复: 可以这样设计的。

    2019-11-21
    1
    1
  • FIGNT
    我们在设计领域模型时,遇到一些问题
    1. 查询聚合的操作应该放在哪一层?
    2. entity的实体和值对象太多需要分目录吗?
    3. 针对实体的维护,需要通过聚合去维护吗?可以直接修改实体吗?
    4. 一个聚合保存在一个库里,还是多个聚合都在一个库里?一个实体需要单独放一个库吗?如果一个实体被修改了。用到这个实体的聚合需要更新吗?
    5. 聚合是设计成单个的还是批处理的?比如一棵树,业务上是以一片叶子为单位的,那么是以树为聚合还是以叶子为聚合?

    作者回复: 1、个人感觉批量大数据量的查询用仓储有点勉强,你可以用传统的方式来做。如果不涉及到领域逻辑的话,可以放应用层。
    2、一个微服务的聚合内部应该不会有太多的实体和值对象吧。在目录结构里面是一个聚合一个代码目录。当然如果实在太多,你是可以再分目录的。
    3、聚合内的实体数据维护是通过聚合根通过仓储来统一维护的。
    4、一个微服务一个库,微服务内的多个聚合可以共用一个库,但是尽量避免聚合之间的表关联,聚合之间的数据要做到松耦合。
    5、不清楚你说的单个和批处理是什么意思?聚合是具有一个完整业务功能的单位,就看你业务的粒度大小。多个不同功能的聚合是可以构成一个比较大的业务模块。

    2019-11-14
    1
  • 瓜瓜
    还有这个依赖倒置,一开始感觉很清晰,现在感觉越看越懵,老师能给再解答下吗?

    作者回复: 我在你的上个问题回复了,不知道解答你的问题了没有。不知道你的疑问在哪里?如果没解答清楚,麻烦告诉一下你的疑惑在哪里哈。

    2019-11-13
    1
    1
  • Farewell丶
    1.应用服务只能调用领域服务和实体的方法,能调用仓储接口的方法么?
    按理说应该隔离,也就是说应用服务应该调用领域服务的方法,再让领域服务调用仓储接口的方法吧?

    2.实体的转换只有从用户接口层到应用服务层一次是么?也就是说,到应用服务层之后,以及之后的仓储接口都是可以直接对领域实体进行操作的?

    3.参考了Spring Data Jdbc项目,里边也采用了DDD的设计思路,但是发现会需要在实体中配置一些和底层存储相关的注解,这样会不会不能把领域层可仓储实现进行隔离?如果是这样的化,那么Spring Data Jdbc是不是没有严格遵守DDD的一些设计?而且它提供的领域事件的发布机制实现,是在对应的实体中产生的,例如在某一个实体中定义产生领域事件的源头,当对应的实体保存或更新时,就会发出这样一个领域事件。按照咱们文章中讲解的事件的发布是在应用层,那么如果要这样做的话,是不是就需要在应用层重新转发领域层实体内产生的领域事件呢?
    因为看到Spring Data这样比较广泛的项目实现和咱们文章的描述有一些我理解上的区别,所以比较困惑和疑问。

    作者回复: 1、如果是应用服务直接调用文件或者缓存之类的,应用服务是可以之间调用仓储的。但如果中间有领域实体和数据库,则需通过领域服务,然后通过聚合根来调用仓储。
    2、用户接口层大多是DTO,应用层和领域层大多是DO,基础层则是PO,在不同层之间是需要进行数据转换的。我有一节专门讲这个。
    3、如果是这样的话,确实领域层与数据库层会有耦合。领域事件其实放领域层也是可以的,放应用层主要是为了统一管理。如果领域事件放在实体内部,查找和运维起来就不是太方便,而且这个实体还需要对领域事件的实体进行操作。目录结构的设计主要是从边界、分层和便利性考虑的。

    2019-11-13
    1
  • 嘉嘉☕
    老师好!
    请问,
    同一个微服务内, 跨领域的方法调用, 我们可以在应用层进行组合和编排,
    那么,
    微服务间的领域方法调用是怎样的呢 ?
    被调用者, 方法是从哪一层暴露出去的 ?
    调用者, 是从哪一层发起调用请求的 ?
    谢谢!

    作者回复: 是从应用层发起的。方法是逐层封装,一直到应用服务。微服务内应尽量避免领域服务在不同聚合之间的调用,这样聚合之间耦合度会比较高。

    2019-12-07
  • OTM
    如果在接口层需要返回:用户名称,积分数,卡券数,向后续应用层传递的不一定是领域对象 返回的业不一定是具体领域,可能是一个聚合服务,那这里返回的对象应该再那里定义,应用层,还是接口曾

    作者回复: 在接口层定义DTO对象。数据可能来源于多个DO对象。

    2019-12-06
    1
  • Geek_aa8017
    老师,跨微服务调用用的是dubbo, dubbo暴露的接口应该定义在哪里

    作者回复: 用户接口层就是用来封装应用服务和对外暴露接口的。

    2019-12-05
    4
  • 瓜瓜
    Entity(实体):它存放聚合根、实体、值对象以及工厂模式(Factory)相关代码;此处的工厂模式,主要是来实现实体吗??这块理解的不是很清晰,望老师帮忙解答。感谢!

    作者回复: 工厂模式主要是实现复杂聚合的实体的数据初始化。如果实体太多,聚合根处理起来会很复杂,通过工厂一次初始化。

    2019-12-03
  • vivi
    老师,像COMMAND,QUERY 层这种CQRS的什么场景下使用呢?

    作者回复: 大量复杂查询的场景下使用。DDD不擅长大量数据查询。其实CQRS查询也可以在同一个微服务内。数据分库也可以,但数据实时性不好保证。

    2019-11-26
  • 杨杰
    关于微服务的用户接口层和应用层有点儿疑问。在整个微服务架构里面一般微服务上层还有BFF层、聚合服务层,一般BFF层或聚合服务层用来协调多个微服务或者做数据转换。那么对于某个具体的微服务是否还需要用户接口层和应用层的区分呢?如果DTO是在用户接口层,那么这些数据如何传入到应用服务层呢?

    作者回复: 你可以这样定位。微服务内的应用层主要处理自己的逻辑编排,bff主要处理微服务之间的逻辑。

    2019-11-18
  • 陈云
    有一个细节,想问下是如何处理的,接口层接收到的DTO对象,里面的字段跨多个DO,你没办法将一个DTO 完全转成一个DO,这里可能一个DTO的一些字段压根DO里就没有,这个时候 如果 从接口层 传到应用层,再传到 领域层?再封装一个VO对象?然后这个VO对象 是属于领域层?

    作者回复: 不同的对象在不同的层转换。用户接口层DTO和DO转换,应用层主要是DO,调外部微服务的服务的时候应用层有dto和do的转换。领域层与基础层之间,在基础层有DO和PO的转换。

    2019-11-17
  • 小美
    今天的文章中写到应用层编排领域服务和外部服务。领域服务可以直接调用外部服务吗?比如一项业务是先根据实体组装调用参数,然后调用外部服务,再根据结果更新前面实体的状态,那这项业务是由领域服务整体实现?还是由应用层来编排,调实体获取参数,调外部服务,调实体更新状态?

    作者回复: 对外还是尽量靠应用服务来实现。
    领域服务也不是不能做,要考虑耦合和对核心逻辑的影响,综合考虑成本吧。

    2019-11-15
  • 美美
    请问下接口层,应用层,领域层,基础层是一个部署单元还是4个部署单元,老师可以截一个真实项目的maven项目结构吗?

    作者回复: 是在一个部署单元,它们合在一起就是一个微服务。

    2019-11-14
  • Todd BD
    只有应用层的方法才允许暴露给接口层吗? 比如聚合的service已经能很好的表达业务的诉求,而且并不需要在应用层进行编排,是否在接口层可以直接调用聚合的service? 还是需要在应用层代理一次供接口层调用?

    作者回复: 松散分层架构是可以的。严格分层架构只能调用它的紧邻的下层。

    2019-11-14
  • Todd BD
    如果我有多个聚合, 比如聚合根A和聚合根B, 从业务的角度讲,可以接受AB间数据的最终一致性,但从数据展示的角度考虑, A和B是有强关联性的,也就是说在页面上,他们总是一起在页面的某部分出现, 那么在应用层是否要在query 接口中把这两个聚合根封装成一个新的对象再返回?
    还是我想的太多了, application层应该以增 删 改这种业务诉求为导向设计, 而query这种诉求应该用类似CQRS中的query model去实现?

    作者回复: 你可以分别调两个聚合的领域服务,然后将两个聚合根的DO对象转换为一个DTO,就可以给前端提供包含两个聚合数据的数据服务了。

    2019-11-14
    2
  • 瓜瓜
    Repository(仓储):它存放所在聚合的查询或持久化领域对象的代码,通常包括仓储接口和仓储实现方法。为了方便聚合的拆分和组合,我们设定了一个原则:一个聚合对应一个仓储。特别说明:按照 DDD 分层架构,仓储实现本应该属于基础层代码,但为了在微服务架构演进时,保证代码拆分和重组的便利性,我是把聚合仓储实现的代码放到了聚合包内。这样,如果需求或者设计发生变化导致聚合需要拆分或重组时,我们就可以将包括核心业务逻辑和仓储代码的聚合包整体迁移,轻松实现微服务架构演进。

    给老师点赞

    作者回复: 😄

    2019-11-14
  • Harvey
    spring-mvc的Controller和Intercepter应该放在哪个目录下呢?

    作者回复: 这些是基础层的组件吧。

    2019-11-14
  • ZIxuAN
    每次通过聚合执行一些操作之前都要先查询聚合,如果这个聚合要从很多表中查数据组装的话性能就很低,请问这里是不是要针对整个聚合根加缓存呢?

    作者回复: 批量的查询我不太建议走聚合根,不走聚合根,用传统方法也行。

    2019-11-13
    4
收起评论
37
返回
顶部