我喜欢看我努力的样子,因为那是幸运的伊始——良木


首先正视一个问题,DDD(Domain Driver Design)是什么?

  • DDD是一套建模思想,用来启发人们对软件架构的设计。
  • DDD是提供了一种交互模式,可以帮助开发人员快速理解业务场景,也可以帮助业务人员快速理解建模用意。
  • DDD可以帮助“新入者(指刚刚接触项目的人)”快速理解项目语义,从而更快的投入开发工作。

DDD能干什么

DDD主要关心如下三个方面:

  1. DDD会将所有的开发人员以及业务人员聚焦在一起,这样开发人员所开发的软件能够真实的反映出领域专家的思想,从而构建出领域模型。领域专家会和开发人员共用一套建模语言去讨论现实中的业务,从而将“他们”与“我们”都变成我们。
  2. DDD更关注业务战略设计,虽说战略自然而然的包含了战术设计,但是战略是业务层面的具体导向,而战术设计则是对于具体业务的具体实现。战略设计会划分出不同的团队以及团队职责,而DDD恰好可以作为一种划分团队的手段,(在我目前工作的环境-供应链中有很多团队,每个团队都有自己项目。我们平时在交流的时候,都习惯称某某域怎么怎么,例如仓储域,物流域等。而这些团队,都有自己所维护的一套系统,每个系统都自己所负责的业务职责。反而言之,就是业务职责规定了团队划分,可以将业务职责理解为DDD中Domain。)
  3. 战术设计建模,DDD满足了软件真正的技术需求,这些战术设计工具满足业务专家的思维模型构建,换言之就是将业务专家所想的东西能够根据DDD模型呈现出来,真正做到思维模型即代码设计。(当然,在我工作途中,这个目的一般会因为开发人员的个人素养,以及领域专家的个人表达能力而被弱化,很难有项目和领域专家的思想百分百契合,除非这个项目是一个人设计的,而这个人即是领域专家,又是开发人员。)这样设计可以大幅度体现出软件的业务价值,让每一次交付都变得有意义。
    那么DDD究竟能干什么呢?
  4. 简化项目的复杂度,降低软件的维护成本。(目前我们团队正在重构一个项目,这个项目负责5条业务线的运维,每条业务线对于部分操作都有其特殊化处理。而我们在重构初期,仔细研究这些业务线所实现的类后发现,每条业务线都包含一个通用的部分,而这个通用的部分在这些实现类中实现了5次,这严重违反了DRY(don’t repeat yourself)原则,像这种重复建设的东西我们最后决定用一个上下文来解决其重复建设问题,同时也简化项目复杂度,缩小了维护和测试范围。顺嘴一说,讲实话项目在DDD重构后,我估计我们团队会流失一部分人,因为维护成本与开发成本的降低了。)
  5. 将业务专家与开发人员拉到统一战线上,而不是分兵作战,将他们和我们都变成我们。(这里主要说的是团队交流,不是团队人员构成)
  6. 可以快速确定软件的业务价值,因为DDD关心战术设计的同时,更关心战略设计。

真的需要DDD吗?

参阅《领域驱动设计》-Eric Evans的著作和《实现领域驱动设计》Vaughn Vernon的著作本想找到一些关于“DDD是否真的需要”的一些文献,发现并没有找到。以下是我个人的看法:
DDD是一种建模思想,并非与微服务绑定,很多人在理解DDD时认为其不过就是微服务的一种划分手段。实则不然,即便是非微服务架构,也可以用DDD进行建模。先给DDD一个定论,DDD是一种建模思想,他与所用语言、技术架构无关。那么,项目是否需要用DDD思想来建模呢?
上面把DDD吹的乌丢乌丢的,下面又说是否需要,可气。
但事实上的确有的项目不需要DDD建模,例如我们团队一个简单的仓库服务系统,其功能非常简单,就是读取仓库信息,插入仓库信息,维护仓库编码映射关系。就这三部分,而且一直稳定,很少有服务变动。就这么简单的一个项目,何须DDD来划分项目而添加不必要的工作量,同时添加很多POJO的转换呢?
我认为用不用DDD,取决于三点:

  1. 项目业务复杂度是否高
  2. 项目业务价值是否值得你去这样做
  3. 项目的变动性是否强
    高的复杂度,抽象才具有意义和价值,才能体现出DDD的强大之处(横向对比吗,可以将建模前后的项目做对比,对比之后就知道抽象的好处了)。较高的业务价值才能带来较高的收益,没有收益做他干甚么?例如我负责的一个项目,就是做做简单的数据库数据整理于上报,没有这个项目,对于公司的业务运营又没有影响,甚至剔除掉这个应用,对于公司来说也是可以接受的,这种项目做什么DDD构建啊。项目的变动性如果不强的话,就没有必要去DDD了,因为稳定大于一切。如果你用DDD重建,重建的比较成功的话,那只过是景上添花。如果失败的话,那你就需要负相应的代价,并通过5w2h去复盘此次失败,这只会让你得不偿失。似乎第三点有点和DDD构建的初衷相互矛盾,对,没错,就是矛盾。是否用DDD完全取决于TL对项目的把控,或者说整个团队的KPI规划,我只是给出了我认为对的三点参考意见而已。

如何DDD

DDD最具有威力的武器便是他的限界上下文(Bounded context)和通用语言(Common language),两个共同构建了DDD的两大支柱,二者相辅相成。

上下文术语 就现在来说,可以将限界上下文看成整个应用程序之内的一个概念性边界,这个边界之内的每种领域术语、词组和橘子——也即通用语言,都有确定的上下文含义。边界之外,这些术语可能表达的含义不同。


通用语言

通用语言就是团队之间的共享语言。领域专家和开发者使用相同的通用语言进行交流。事实上,团队中每个人都使用相同的通用语言。不管你的团队中的角色如何,只要你是团队中的一员,你都将使用通用语言。

那么,你认为的你已经知道了什么是通用语言? 很明显,通用语言是一种业务语言。 抱歉不是。 通用语言必须采用工业标准术语。 不完全是。 通用语言是给领域专家用的。 对不起,不是。 通用语言是团队自己创建的公用语言,团队中同时包含了领域专家和软件开发人员。 对了。

以上摘录自《实现领域驱动设计》Vaughn Vernon的著作,因为我感觉他的这段表述很准确,很到位。
那么,放在现实工作中,通用语言是什么?其实没有DDD就没有通用语言了吗?答案不是的,我对通用语言的理解是——团队对整个业务的共识,以及这些共识点所组成的实体就是通用语言。举个简单的例子,订单,在我所处的仓储管理团队中,订单就是出入库任务,无论给我们域的任何成员说订单信息,他们都会立马反映到出入库任务上。这些大家相互达成共识的东西,就是通用语言。如果将订单放在淘宝平台上,订单就是客户下达的购货任务。如果将订单放在商家销售上,又成了销售任务。很多狗逼将通用语言说的这样解释,那样解释,到最后还不是团队共识?吐槽一下,曾经学DDD时看书上的通用语言没啥影响,去找资料看时,他们都说什么什么特性之类的话。但当实际在公司推行DDD时,仔细思考了解后发现,TL说的通用语言就是团队共识的抽象组成。


识别通用语言的办法

通用语言的识别其实就是一次次业务抽象所堆积起来的成果。我这里有两个办法来识别通用语言,如下:

  1. 简单快速的方法——领域专家直接制定通用语言,然后由开发人员配合领域专家商讨细节,最终确定整个域的通用约束。当然,这种办法最适合那种新建项目的情况,因为没有业务积累,没有知识储备,所有的信息都来源于领域专家,在这种情况下,领域专家的意图便是整个项目的意图。
  2. 稍微复杂,但行之有效的方法——将业务活动进行枚举罗列,然后抽取其中通用的部分,并将这种通用的部分概念化,从而形成通用语言。这种方法稍微麻烦,需要罗列出业务活动在代码中的实现方式,简而言之就是将代码的运行流程绘制出来。这种方法会使开发人员的参与度比较高,从而在后续的通用语言推进过程中获取更高的认可程度。当然,这种办法最适合那种老项目需要重构的情况,因为已经有大量的业务积累和知识储备,无非就是怎么消除团队成员之间的知识差异和重新抽象出更高的语言级别而已。每个成员对于业务已经有了自己的认知,在这种认知下去颠覆一个人的看法是很难得,而且也是不容易被团队接受的。所以采取共建的办法,就可以提高团队成员的参与度,从而达到通用语言的普适性(说白了就是认可度,他自己抽象的,他能不支持?有句话说的好,不需要知道火车去哪,只要他自己坐上了,那么他必然身不由己)。

基于第二种方法,我提供几个步骤供参考:
step1:将现有业务流程,最好是和现有代码有直接关联关系的业务实现流程绘制出来。在这一步,要求尽可能的细致,任何场景,任何特性都不要放过。提供的工具有,UML的时序图(其实活动图更适合),泳道流程图等都行。当然,什么都可以不用,直接自己定义一套流程图也是可以的。
step2:将业务共性的地方圈出来,并给这些地方下定义。不要害怕自己的定义不完美,在最后review的时候,可以征求团队成员意见,共同定义。
step3:抽象语义,形成术语表,并在文档上表述描绘。


提供一个我自己用来表述业务流程而构建的图:
IMS入库组件流程.svg
不必拘泥于形式,只要能准确的表述你业务用意和达到你的目的,那么便是好图。


是通用,不是“银弹”

软件设计没有银弹——《微服务架构与设计》一书中经常提到概念,在DDD中,他同样适用。关于“通用语言”我还想再表述一下:

  • 通用语言的通用指的是团队共识,只局限于团队内部,而不是团队之间的沟通交互,与其他公司企业的定义可能相差甚远,但你并不需要关心其他公司的定义,你只需要知道他能满足你的业务需求即可。
  • 界限上下文(md,别的文档都是限界上下文,当我感觉还是别扭,我自己就定义为界限上下文,只要我能理解是啥意思,同时让别人也能理解我的意思即可)和通用语言是一对一关系,即界限上下文规定了通用语言的适用范围,界限之内通用语言绝对适用,界限之外则与我(通用语言)无关。
  • 团队只有在一个界限上下文工作时,才能说自己的通用语言是通用的。——摘录《实现领域驱动设计》Vaughn Vernon的著作
  • 术语重叠时,就需要靠界限上下文划分边界。这非常重要,因为同样的术语在不同的团队中有不同的语义,例如维保,在保险公司意味着保险赔偿,在司法机关则意味着法律维权,不同地方的同样语言,可能语义相差甚大。
  • 界限上下文中只使用通用语言,非通用语言则拒绝使用。

使用DDD所带来的价值

——本节内容均摘录自《实现领域驱动设计》Vaughn Vernon的著作(因为我觉得还是这本书表述的更加合适且具体,我自己受限于经验与文采,还是不敢献丑。文中会将啰嗦的话语进行缩水处理,和原文可能稍有出入)
如果你的经验与我想当,你就应该知道软件开发者不应该局限于技术,而应该将眼界放的更宽。无论什么技术,都应该以产生业务价值为目的,人们没理由拒绝一项能带来业务价值的技术。如果我提供的技术方案比其他技术方案更能带来业务价值,则我的业务能力也会相应增强。

业务价值重要吗? 业务价值非常重要,软件说白了就是赚钱的。软件服务于业务,业务服务于老板,老板只是为了赚钱。过多的理论和技术如果不能获得更大的报酬,我相信老板没有理由去采用新技术而去浪费自己的经济成本。一句话,只要能赚钱,就是好技术。——业务价值等同于money

那DDD能带来那些业务价值呢?总共有如下几点:

  1. 你获得了一个非常有用的模型
  2. 你的业务得到了更准确的表述与定义
  3. 领域专家也可以为软件做出设计与贡献
  4. 更好的用户体验
  5. 更好的企业架构
  6. 敏捷、迭代式和持续建模
  7. 使用战略和战术新工具

    使用DDD所带来的挑战

    《实现领域驱动设计》Vaughn Vernon书中有提到过其挑战,但是我感觉并不是很适合中国这种市场环境。好吧,我承认我夸大事实了,我承认他的表述不适合我自身的经历。我认为DDD所带来的挑战如下:

  8. 难以抽象的通用语言和难以划分的界限上下文,我们团队在构建界限上下文和通用语言时,花了接近多半个月的时间,这还是在已经有业务基础和团队共识的情况下。其中问题最多的就是同一语言在不同场景的不同语义,而负责两个不同语义的开发人员相互争执不下。

  9. 很难获取测试资源,因为你的项目重构只能保证自己的团队去执行某一任务,而其他团队的成员才不关心你们团队项目的死活。你的上游只关心你能不能提供服务,你的下游只关心你能不能下达任务指令,你的测试只关心你能不能保证业务流水,而你却需要保证你的项目稳定,业务支持不受限等。跨团队资源很难争取,这是非常大的问题。
  10. 开发者不认同你的通用语言,团队成员难以摆脱原本语义约束。这种情况虽说会随着时间流失而流逝,但是在建模实现的初期,这种情况非常常见。
  11. 老板和其他团队成员的质疑,其他团队的质疑很好解决,重构自己的项目与其他团队无关,只要能保证业务流水正常进行,他们自然而然就会闭嘴。但是老板的质疑却很致命,因为老板要负一定的业务责任,如果你的理由无法说服老板,那么你最好还是不要采用DDD。如果老板同意了DDD,你在实施完毕后,老板还会质疑你的模型能带来那些价值,但是关于模型的价值,只有在长期不断地磨合中才能体现,短时间很难体现出其价值。这就导致有一种错觉,你的模型毫无意义,并没有增强团队的工作效率。
  12. 最大的一个挑战,也是让我最为恼火的地方。团队成员很难自驱性去提高自己的知识素养,并且用你的那一套去完成他们的业务。他们很乐于去沉寂在自己的世界中,采用自己才能理解的类去实现他们的业务需求,他们的开发会破坏你的模型结构,从而导致你的模型在后期开发中越来越烂,最终演变成一个“大泥球”。如果老板此时再来一次灵魂质问,你的项目怎么运行的那么慢,结构怎么那么乱,交付周期怎么那么长,稳定周期怎么那么短?你要作何解释?说因为团队成员的开发素养而导致自己的项目很乱?那你得收拾行李去下一家公司报道了,老板才不关心过程,一切都已结果为导向。解决开发问题的最好办法是更改上线流程模式,每次开发上线都应该经过有素养的开发人员进行代码review,从而避免泥球的发生。