我在GitChat发布了《领域驱动设计实践》战略篇+战术篇课程,购买该课程的学员还可以免费加入由我创建,GitChat维护的DDD微信群。目前,已创建三个微信群,人数达到1200人左右。在这三个群里,每天都有DDD的爱好者参与激烈的讨论,讨论的质量也格外的高。我几乎可以自豪地说,天下DDD英雄已尽在群中矣。
感谢志愿者@子鱼每天不辞辛劳对群里问题的收集。本文遴选了部分问题给以回答。
DDD事件风暴如何落地
背景:电商履约过程中, 确定一个订单具体进哪一个仓的因素多, 以往的实现方式是把这些规则收集起来逐个实现。
问题:针对订单定仓的场景, 事件风暴中, 可能只有一句话, 表示成“订单已定仓”。背后那些复杂的规则及规则之间的综合判断逻辑,没有体现出来, 也就不方便用DDD的思路改造。请帮看下, 这样业务上可能一带而过但具体实现复杂的功能点, 怎么借助DDD改造优化下?
提问者之后又补充道:
具体实施上有些问题:
公司没有领域专家。
有业务方, 不过业务方不稳定, 经常换人, 反倒不如落到代码上的经验具体和全面。
我的回答:
实际上这个问题充分说明我们在实践事件风暴时,并没有得到合理运用。事件风暴究竟优势在哪里?事件并非灵丹妙药,因为有了它,业务就一下子变得清晰了。哪有这么容易的事儿。如果真正实践过事件风暴,你会发现它的核心思想其实仍然是通过识别出领域知识中各个细小但关键的概念,然后针对这些概念进行抽象、归纳,算是一种自底向上的分析过程,只不过表现形式不再是功能、用例、或者类。
实践事件风暴与传统建模方法不同之处在于:
- 它非常强调领域专家与全团队的参与。如果公司没有领域专家,也需要邀请具有业务知识的角色参与到事件风暴的过程中来。
- 它非常强调可视化的沟通形式与面对面的交流形式。利用各种颜色的即时贴表示不同的概念,并以清晰直观地事件流呈现出来,就能消除交流的误解,打破沟通可能存在的坚冰。
至于事件产生的驱动力,以及事件风暴的过程,在我的课程中已有介绍,这里就不再赘述。
核心子领域还是支撑子领域
问题:请问各位大佬,公司要做促销系统, 在下准备根据ddd的思想来做, ,但是想到要划分核心域, 通用域,支撑域的时候,针对于公司,整个促销系统都不是核心 , 是不是这里就直接整个系统都当做一个支撑域就行了?
我的回答:
这个问题也是许多初涉DDD的人容易误解的。Eric Evans在讨论核心领域时,当然是针对你要开发的系统而言,只有对你的系统而言最有价值,最值得投资的领域才是你的核心领域。Vernon在《实现领域驱动设计》书中,就阐释得更清晰一些,他认为分辨子领域到底是核心,还是通用或者支撑,需要结合具体的场景来考虑。例如在电商系统中,地图领域就是支撑子领域,但是对于做地图系统的团队而言,地图领域就是核心子领域了。
促销系统或许不是公司的核心领域,但是这里要实施DDD的是促销系统,毫无疑问,你应该为促销系统划分核心子领域、支撑子领域以及通用子领域。除非整个领域的问题域非常小,自然没有拆分的必要。
顺便说以下,Eric Evans在《领域驱动设计》书中仅仅提出了核心领域(注意是核心领域,而非核心子领域)与通用子领域,Vernon的书中则增加了支撑子领域。为了概念的一致性,统一称之为:核心子领域、支撑子领域与通用子领域。
六边形架构
问题:我们的定向开发思维,导致在对分层架构时很好理解;但对于六边型架构,虽然看过很多次,但还是不能想像出具体的代码实现。请问六边型架构有具体的源码吗?
回答:
群里已有精彩的回答。
Qiao Liang: 洋葱架构 https://youtu.be/o_TH-Y78tt4 bob martin讲的挺清楚的
忘却录音:整洁架构,微内核架构,架构设计模式 都是通过分离关注点,划分边界的方式。达到结构的清晰与一致
lucoo:阿里的cola https://github.com/alibaba/COLA
孔令秋:六边形架构的核心是隔离耦合,六边形架构将系统分为两个部分,系统的业务逻辑与系统依赖的其他部分(其他部分包括各种中间件,其他的微服务,数据库等等),六边形架构最好结合maven来使用,一个微服务对应一个project,一个project多个module,最核心的三个module分别是应用层,领域层,基础设施层,其中基础设施层引用其他两层,这样就实现了依赖倒置,依赖倒置也就隔离了耦合,同时单元测试也会变得容易
阿斌哥: 六边形架构的入站适配器对应张老师所说的北向网关,出站适配器对应南向网关,入站端口对应应用服务,出站端口对应南向网关的抽象。依赖倒置的核心就在那一层南向网关的抽象。
我的补充:
六边形架构其实相当于是隔离内外的分层架构,在学习时,你可以将两个六边形隔离出来的三个区域映射到分层架构上帮助你理解。我的GitChat课程其实已经较清晰地分析了分层架构、六边形架构与整洁架构之间的关系,以及分层架构的演进。
在内外两个六边形之间的区域都是适配器(Adapter)。如果是六边形外部向内部的适配器发起请求,则该适配器称之为Driving Adapter,也就是我说的“北向网关”;反之,该适配器称之为Driven Adapter,也就是我说的“南向网关”。
如果结合DDD的模式来理解,北向网关就是上下文映射中的“开放主机服务(OHS)”模式,服务可以分为:
- Resource:REST资源服务
- Provider:RPC提供者服务
- Controller:面向前端UI的控制器服务
- Event Subscriber:面向事件的订阅者
这些服务传递的DTO(我称之为消息契约对象)就是上下文映射中的“发布语言(Published Language)”模式。
南向网关就是上下文映射中的“防腐层(ACL)”模式,常见的防腐层包括:
- Client:对被调用(上游)服务接口的封装
- Event Publisher:发布事件
实际上,我们也可以将对数据库访问的封装即资源库(Repository)视为是一种防腐层。我在GitHub上创建的项目EAS-DDD的代码结构其实就是按照这一思路来创建的,也可以认为它遵循了六边形架构的设计原则。
目前,我看到讲解六边形模式(即端口适配器模式)最好的是ThoughtWorks的周宇刚撰写的文章《端口和适配器架构——DDD好帮手》。本文发表于ThoughtWorks洞见,讲解思路非常清晰,作者对该模式的理解炉火纯青,真的是从本质上剖析了该模式与DDD的结合。推荐阅读。