网上有非常多关于DDD的文章,这当然是好事情,大家都想掌握好的设计方法来解决软件开发中的问题。但是这其中也存在一些问题,如果你随便打开网上的几篇DDD文章,虽然每一位作者都说自己是按照DDD思路进行架构设计的,但是细心的同学会发现每一个作者DDD文章中的结构描述、画的架构图都千差万别,你会非常奇怪,这些都是DDD设计吗?这里其实有一个问题,就是通过文字和图示描述一些抽象的概念时,本来就会有很大的差别。大家不要用盲人摸象的概念进行类比,这个不太合适,即便两个同学,对DDD都非常了解,而且都实践了好几年多个项目,他们写出来的东西还是不一样。我Java入行稍微早点,当然你说我年纪大保守也可以,记得当初没有那么多中间件,就是基于Struts 1.x这个MVC框架进行开发,不同的同学写出的设计文档也是千差万别。这么简单的MVC架构都能有不同的架构设计文档,而DDD相对更抽象、更难以理解,所以架构设计文档长的不太一样,这个也是可以理解的。
那么我们是不是要接受这个事实,“各个作者对DDD的解释可以不必相同”,"DDD设计文档可以以不同种形式呈现"?如果是这样,那么想学习DDD的同学就有非常大的负担,哪种设计表现方式才是比较好的,才是比较容易理解的,同时我怎么知道我学的DDD是相对正统的,没有被别人带歪。我不是说发挥性思考不可以,但是从传道的角度来说,尊重理论事实还是需要的。
我们都知道代码在表达一些业务或者逻辑时,非常能反应真实情况,即便是不同的开发者所编写,考虑到遵循Design Pattern、命名规范、开发语言约束等,代码大体上还是相同的,还是便于理解的,如果有单元测试和Code Review,那就更好啦。这也是在一些文档不完善的时候,非常多同学选择阅读代码,更有同学说,“直接看代码,不要看他们PPT和文档,会被误导的,不然怎么死的都不知道”。另外我们都知道,现在一个非常好的实践就是Everything as Code,典型的如Infrastructure as Code的Terraform,Platform as code的Kubernetes YAML, Diagram as Code的PlantUML等等, 那么我们能否使用DDD as Code这个概念,让我们的设计更统一些,更能方便表达设计思想,更容易被人理解。
DDD DSL
用DSL也就是用代码方式来表达DDD,这个很早就有了,但是更偏向DDD的战术设计(Tactic Design)和代码层面,如 Sculptor[1]和http://fuin.org DDD DSL[2] ,大家普遍都认为,就是基于Xtext的DDD代码生成器。要费劲学习那么多,只为生成一些代码,而且只是Java代码,所以普遍关注度并没有多高。
我们能否将DDD DSL除了代码生成这一部分,更偏向于战略设计(Strategic Design),突出设计的思想,那么DDD as Code就全面多了。接下来我们就介绍ContextMapper这个框架。
名词解释一下:有不少同学对于DDD的战略设计(Strategic Design) 和战术(Tactic Design)之间区分有些疑惑,DDD有专门的介绍,如下:
- 战术设计(Tactic DDD):Entity, Value Object; Aggregate, Root Entity, Service, Domain Event; Factory, Repository。
- 战略设计(Strategic DDD):Bounded Context, Context Map; Published Language, Shared Kernel, Open Host Service, Customer-Supplier, Conformist, Anti Corruption Layer (context relationship types)。
其实也比较简单,战略设计更大一些,偏宏观,你可以理解为公司高层在讨论的业务和技术方向,各个团队或者产品的分工和配合;而战术设计则相对小很多,主要集中在一个BoundedContext内部,比如如何设计DDD那些Entity,Service,Repository等,外加可能的应用开发的技术选型,可以说更关注技术层面。
ContextMapper框架介绍
ContextMapper是一个开源项目[3] ,主要是为DDD设计提供DSL支持,如DDD的战略(Strategic)设计,Context间映射(Context Mapping),BoundedContext建模以及服务解耦(Service Decomposition),那么我们就看一下如何基于ContextMapper来完成一个项目基于DDD DSL的表达。
在介绍ContextMapper时,我们先交代一下项目背景。如花是一名架构师,对DDD也非常熟悉,而且有过几个项目的DDD实践,最近他加入会员线,负责完成对会员系统的改造,更好地配合公司的微服务化的设计思路。会员线之前就是三个应用:会员中心对外提供的大量的REST API服务;会员注册和登录应用;会员中心,处理会员登录后如修改个人密码、基本信息、SNS第三方绑定和支付方式绑定等。
如花加入会员团队后,和大家沟通了基于DDD + MicroServices的架构思想,大家都表示同意,但是如何落实到具体的架构设计和文档上,大家就犯难啦。让我们看一下最典型的DDD设计图:
其中的概念,如SubDomain、BoundedContext、Entity、ValueObject、Service、 Repository、Domain Event,以及Context映射关系(Context Mapping),这些都没有问题,但是如何向他人表达这个思想?总不能每次都把DDD设计图和分层图都贴上去,然后说我就是按照DDD设计的。
从SubDomain开始
如花开始DDD的第一步,也就是Subdomain的划分。当然DDD中包括三种类型的SubDomain,分别为通用(Generic)、支