DDD:架构思想的旧瓶新酒
The following article is from InfoQ Author 唐智
作者丨小智
DDD 和 DSL、DCI 的关系是什么?开发团队为何需要 DDD?它与微服务与中台又有着怎样的联系?目前业界实践 DDD 最大的问题是什么?11 月 30 日,在由 ThoughtWorks 举办的领域驱动设计峰会 DDD-China 2019 上,InfoQ 记者带着这些问题对中兴通讯资深软件架构师张晓龙进行了采访。
DDD 概念最早提出于 2004 年,作为一种软件开发的指导思想,DDD 对软件开发带来了诸多可能与方向,张晓龙认为 DDD 为软件开发带来的好处主要有以下几点:
首先,最大好处就是所有参与者围绕一个统一一致的领域模型工作,传统的分析模型和设计模型不再割裂,不管是做设计、做分析还是写代码、写文档,脑海中所构建的画面都是一致的。
第二,DDD 是一个软件开发过程,它显式地把领域和设计放到了软件开发的核心,软件人员和业务人员被受到同样的重视,他们合作来构建领域模型,使得软件的交付质量更高且维护成本更低;
第三,DDD 提出的分层架构,有效分离了业务复杂度和技术复杂度,凸显了领域模型,使得领域层的代码和领域模型保持高度一致;
第四,统一语言非常重要,每个概念在各自的上下文中是清晰的无歧义的,同时要控制领域模型的复杂度,于是 DDD 在战略上提出了分离子域(问题域空间)和拆分 BC(解决方案空间)的模式,BC 间通过 Context Mapping 来集成;
第五,DDD 在战术层面提出了很多模式(聚合,实体,值对象,服务,工厂,仓储),对领域模型中的元素进行了分类,并给出了每类元素在领域模型中的职责和特征,降低了领域模型的构建成本。
张晓龙此前曾在 DDD-China 峰会和 ArchSummit 全球架构师峰会上分别做过《当 DDD 遇上 DSL(Domain-Specific Language)》、《当 DDD 遇上 DCI(Data,Context, Interactive)》的演讲,在他看来,DDD 和 DSL、DCI 之间存在极强的关联性。
DDD 和 DSL 的融合有三点:
面向领域;
模型的组装方式;
分层架构演进。
DSL 可以看作是在领域模型之上的一层外壳,可以显著增强领域模型的能力。它的价值主要有两个,一是提升了开发人员的生产力,二是增进了开发人员与领域专家的沟通。举个例子:想让 BA 负责流程契约的设计,该流程契约是一个活文档,可以跑测试,而 BA 不熟悉宿主语言。于是,我们设计了一种外部 DSL 来专门描述流程契约,对 BA 非常友好,学习成本也很低(不超过 5 分钟就可以学会),最后发现 BA 很快就广泛使用了起来。外部 DSL 并不一定要定义新文法,我们直接复用了 plantUML 文法,安装该插件可以自动生成序列图,非常棒!对于外部 DSL,需要自己实现一个解析器将 DSL 文法解析成语法树,再根据语法树生成语义模型。语义模型可以看作领域模型(严格的讲语义模型是领域模型的子集),外部 DSL 就是对领域模型的一种组装方式。
DCI 的作用主要体现在两方面:
首先,DCI 助力 DDD 战术设计:
显式的对 ROLE 建模,解决了贫血模型与充血模型之争;
一个聚合可以支持哪些 ROLE,一个 ROLE 可以由哪些聚合扮演,一个场景下哪些聚合要扮演哪些角色;
当 Aggregate 内部实体行为比较多时可以嵌套使用 DCI 来拆分和组合;
其次,DCI 助力 DDD 代码落地:
对象就是 Data,Client 为 Context,对象在 Client 中的行为就是 ROLE。
根据正交设计原则得到小类(素材库),根据多重继承(only C++)或依赖注入来组合素材,不管是行为类还是数据类,都按 Role 的方式来组合,对像仅仅组合 Role 并注入依赖;
小类大对象:类作为一种模块化手段,遵循高内聚,低耦合,让软件易于应对变化;对象作为一种领域对象的的直接映射,解决了过多的类带来的可理解性问题,让领域可以指导设计,设计真正反映领域;领域对象需要真正意义上的生命周期管理。
张晓龙认为,DCI 对一些开发人员的影响可能比 DDD 和 DSL 还大,因为开发人员每天都在不断倒腾代码,想让代码的组合性更强,以便快速应对需求的变化。
DDD 思想贯穿了整个软件开发的生命周期,包括对需求的分析、建模、架构、设计,和最终的代码实现,甚至对代码的测试与重构。代码是业务的核心资产,不管是否特性团队,开发团队肯定是代码的编写者和守护者。
对于开发团队而言,需要关注以下几点:
首先是统一语言,让团队成员可以做到无障碍的沟通,不管是什么角色都能基于同样的画面进行讨论;
其次是团队中各个角色都围绕领域模型开展工作;
第三是代码物理设计容易标准化,比如说在分层设计时,基础设施层怎么设计,应用层怎么设计,DTO 应该放在哪儿,领域层中各个建模元素如何组织?
更进一步,在分层架构里,应用层更加关注横切面的东西,比如说要上报一个告警,要给用户发送一个 Email,这些最好都集中放到应用层里面。但触发是在领域层发生的,应用层怎么知道?通过领域事件来实现依赖反转,即应用层订阅领域事件,领域层发布领域事件。
在中兴通讯,核心业务属于通信行业,DDD 的应用场景跟互联网企业有着很大差别:
嵌入式软件;
兼业务复杂性和技术复杂性;
软件规模大,功能复杂,特性交叉;
高质量,高性能,高可靠等要求。
张晓龙举例提到,中兴通讯在开发团队中实践 DDD 的经验具体而言有以下几点:领域专家下团队,和团队一起交流和协作;教练指导,开展战训营,定期 review;架构、设计、编码和工程实践:
(1)DCI,DSL,正交设计,组合式设计;
(2)编码规范和纪律;
(3)嵌入式 C/C++ 最佳实践;软件工程能力:开发者测试,小步安全流畅的重构,持续交付流水线,每日 Code Review。
DDD 概念提出距今已经有 15 年的历史,前十年时间都一直处于不温不火的状态,而在最近几年才开始大行其道。张晓龙表示,中兴通讯在 2012-2015 年期间也有过一些成功的案例,但对于整个业界来说了解的人并不多。他拿 DDD-China 峰会举例解释:这次峰会的参会者有 500 人的规模,而我们假设峰会在 2015 年之前举办的话,估计参会者不会超过 100 人。因此,我们可以断定是微服务的热风让人们才重新发现了领域驱动设计的价值。
微服务架构从提出以来一直没有很好的理论支撑如何合理地划分服务边界,人们常常为服务要划分多大而争吵不休。而 DDD 被发现恰好可以弥补微服务的营养不良:(1)服务最大不要大过一个 BC,否则服务内可能会存在有歧义的领域概念;(2)服务最小不要小过一个聚合,否则会引入分布式事务的复杂度;(3)服务间最好通过 Domain Event 来进行交互,这样可以让服务保持松耦合。微服务和 DDD 的结合,让微服务架构看起来似乎更加稳健了。
“微服务就像是 DDD 的心上人,使得 DDD 真正焕发起了青春。”张晓龙这样解释。
对于业界目前流行的中台概念,张晓龙同样也有自己的看法:
中台和 DDD 不是同一个层面的东西,不能为了把它们联系在一起,而强行找相似点。中台实际上就是多条业务线的共同需求,比如对于滴滴公司来说,快车、专车和出租车等业务都是微服务架构,这些业务的很多服务是相似的,考虑将这些服务从各个前台下沉到统一的平台,这个平台就是中台。中台要考虑各个前台的需求,所以复杂性变高了。
中台是一种企业级的架构模式,从企业全局整体视角来看架构全貌,而 DDD 是一种主流的软件开发方法,用来应对软件的核心复杂性。中台架构可以看作是微服务架构的延伸和发展,服务复杂性很高,所以更需要用 DDD 的方式去设计和建模,但二者之间并不是相同层面的概念。
最近几年 DDD 的火爆也给业界开发团队带来了一些迷思,为什么我的 DDD 推行不下去?为什么我的 DDD 做起来总是跟敏捷一样,最后都变了味?
张晓龙总结了 DDD 目前面临的几大困局:
首先是领域案例面比较窄。目前业界的 DDD 实践案例并不多,而且很多案例是偏向互联网领域的,对于工业领域、嵌入式领域和操作系统领域基本没有涉及;
第二,DDD 书籍非常少,而且大多数书籍是以 Java 或 C# 写的。如果开发团队用的是 C、C++、Python 或 Go 语言,基本没有可参考的书籍,难度也就更大一些(尤其是 C 和 C++);
第三,各个巨头公司,比如 Google,微软,BAT 等,很少组织、参与或赞助 DDD 峰会,没有形成引导作用,业界自然也就少有跟随效应;
第四,开发团队要么找不到领域专家,要么领域专家无法与开发团队长时间保持沟通,导致实践中出现偏差;
第五,DDD 落地有一定的门槛,对开发者的技能和素质都有较高的要求。
针对以上几大困局,张晓龙也给出了自己的解决方案:
培训 OOA、OOD 和 OOP 的基本知识,并实战演练,不断弥补与高手的 gap ;
领域专家和团队一起工作,确保大家头脑中的画面是一致的;
DDD 建模要有文档交付物,并和代码同步演进,以便对代码不熟悉的人员也能看到并理解领域驱动设计成果的全貌。
软件开发没有银弹,DDD 也不是万能的。如果开发团队真的决定用 DDD 的思想指导软件开发,就一定要跟随时代的脚步,吃透 DDD 这个旧瓶里装的新酒。
往期推荐
技术琐话
以分布式设计、架构、体系思想为基础,兼论研发相关的点点滴滴,不限于代码、质量体系和研发管理。本号由坐馆老司机技术团队维护。