深入实践DDD:以DSL驱动复杂软件开发
上QQ阅读APP看书,第一时间看更新

前言

为什么要写这本书

2004年,DDD(领域驱动设计)这一软件开发的方法与愿景经由建模专家Eric Evans的经典著作Domain-Driven Design:Tackling Complexity in the Heart of Software正式面世,当即获得了广泛关注和高度评价。16年过去了,我在网上看到越来越多关于DDD的文章和讨论。为什么我们现在还不停地讨论DDD?为什么DDD仍然如此重要?

在商业组织中,主张“技术为业务服务”的企业总可以在理论上立于不败之地。诚然,DDD主张在软件项目中把领域本身作为关注的焦点(换句话说就是技术人员要懂业务)符合这种思想,但真正难能可贵的是,DDD提供了切实可行的应对软件核心复杂性的方法。

实践证明,DDD提出的方法不仅行之有效,而且历久弥新。关于这一点,我想从当今IT业界的热词“云原生”“中台”“产业互联网”说起。

什么是云原生?云原生计算基金会(Cloud Native Computing Foundation,CNCF)[1]对云原生的定义是:

云原生技术有利于各组织在公有云、私有云和混合云等新型动态环境中构建和运行可弹性扩展的应用。云原生的代表技术包括容器、服务网格、微服务、不可变基础设施和声明式API。

这些技术能够构建容错性好、易于管理和便于观察的松耦合系统。结合可靠的自动化手段,云原生技术使工程师能够轻松地对系统做出频繁和可预测的重大变更。

而阿里云发布的《云原生架构白皮书》[2]对云原生架构的定义是:

从技术的角度看,云原生架构是基于云原生技术的一组架构原则和设计模式的集合,旨在将云应用中的非业务代码部分进行最大化剥离,从而让云设施接管应用中原有的大量非功能特性(如弹性、韧性、安全、可观测性、灰度等),使业务不再有非功能性业务中断困扰的同时,具备轻量、敏捷、高度自动化的特点。

看了这些定义,你是否还是觉得迷惑?打开CNCF的“landscape”页面[3]——里面有很多的项目和成员,难怪有人说云原生是一个“营销词语”。在这个页面中,在Members(成员)这个标签页的左边,Serverless独占了一个标签页,十分显眼。

广泛认同的Serverless架构是指这样的应用设计:与第三方的后端即服务(Backend as a Service,BaaS)交互;在函数即服务(Function as a Service,FaaS)平台上运行函数式业务代码,一般来说,它们是在受管理的、临时性的容器中执行的。

虽然新出现的基于容器的Serverless平台,比如Knative[4],可以运行开发人员使用传统方式开发的应用,但FaaS仍然是Serverless中最重要、最具代表性的产品形态,因为它让我们以一种不同于传统的方式思考技术架构。

FaaS中的“Function”就是不依赖特定框架和类库的最纯粹的业务代码——这是真正为业务带来价值的东西。可以说,FaaS在将云应用中的非业务代码部分进行最大化剥离方面做到了极致。我认为它是云原生“皇冠上的明珠”。

不过,大家普遍认为当前FaaS更适合开发事件驱动风格的、处理少数几个事件类型的应用组件,而不适合开发传统的具有很多入口的同步请求/响应风格的应用组件。也就是说,如果你想问:“能不能基于FaaS做出一个SAP ERP?”目前可能大多数人给你的答案会是“NO”。

但是我想给你的答案是“YES”!因为要想达到这个目标,我们需要克服的所有障碍都不属于FaaS的固有缺点,而是当前FaaS的实现缺陷,比如启动延迟、集成测试、调试、交付、监控与观测等方面的问题。

我认为,以DDD方法实现的应用可以极大地降低FaaS处理这些问题的难度,甚至可以直接忽视某些问题(因为它们对以DDD方法实现的应用来说不是问题)。具体而言,我们可以使用DDD的聚合概念来切分应用组件,每个聚合一个小组件,它们可以很快地被FaaS平台“拉起”。这些高度内聚的小组件是更复杂的应用组件(比如说领域服务)的构造块。我们可以使用兴起于DDD社区的Event Sourcing(事件溯源,ES)模式,保证应用状态的每一次变更都会发布领域事件,并以富含业务语义的事件驱动其他应用组件运行。比如,命令查询职责分离(Command Query Responsibility Segregation,CQRS)模式中的Denormalizer(去规范化器)组件就可以订阅、消费这些事件,为前端应用构建友好的查询视图——这些都是DDD社区在开发严肃的商业软件时一直在做的事情。如果之前你没有接触过聚合、ES、CQRS,也许难以理解上面所说的内容,不过没关系,读完本书,我相信你就清楚了。

再说“中台”这个热词。以我的理解,中台是将可复用的代码抽取到一个平台中,作为大家共用的软件组件,它是服务于前台的规模化创新。中台(这里主要指业务中台)想要好用,必须具备“反映对领域的深度认知”的软件模型,甚至在某种程度上需要“过度设计”,并且绝对有必要维护良好的概念完整性,构建所谓的企业级业务架构——这些都是DDD可以大展身手的地方。

关于“产业互联网即将进入黄金时代”的说法,大多是众多传统企业希望借力最新的信息化,特别是互联网工具(即所谓“互联网+”),提升内部效率和对外服务的能力。传统产业中的很多领域概念和业务流程并不一定为普通的开发者所熟知——这与“消费互联网”不同,显然人人都是消费者。所以,传统产业的信息化急需可以快速梳理并深刻地认知领域,以及能构建高质量领域模型的技术人才。DDD可以说是技术人员升职加薪的“神兵利器”。

我在工作中看到的情况是,越来越多的技术人员在自己的求职简历上写上了“熟悉(或精通)DDD”的描述。确实,Eric Evans的经典著作以抽象、凝练著称,可谓字字珠玑,甚至很多资深技术人员都不能领悟其中玄妙。所以,我也认同掌握DDD是一件足以让技术人员引以为傲的事情。

可以说,DDD是公认的解决软件核心复杂性的“大杀器”。但是,在软件开发中实践DDD是需要付出相当大的成本的。也就是说,大家的普遍看法是:实践DDD是一个先苦后甜的过程,一个项目要不要采用DDD,最好先看看它值不值得。

但是一个项目值不值得使用DDD有时不好判断。大项目往往是由小项目发展而来的,很多从小项目演化而来的大系统最终变成开发团队的噩梦,噩梦的根源几乎无一例外地在于软件的概念完整性遭到了破坏。而DDD正是维护软件概念完整性的良药。如果在项目中实践DDD的成本不高,那么即使是小项目,从一开始就使用DDD不是一件很美好的事情吗?

在软件开发项目中实践DDD到底有何难处?16年了,难道在DDD实践中碰到的问题我们不能从书本中寻得答案?

目前国内已经出版的DDD相关图书中,除了Eric Evans的经典著作之外,还有《实现领域驱动设计》《领域驱动设计精粹》《领域驱动设计模式、原理与实践》等,不过寥寥数本。这与DDD的巨大声望很不匹配,这也许说明了一些问题。

实践DDD首先需要面对的一个(也许是最大的)难题是:难以描述的领域模型。

DDD想要构建的领域模型是什么?按照Eric Evans的观点,领域模型不是一幅具体的图,而是那幅图想要传达的思想;不是一个领域专家头脑中的知识,而是那些经过严格组织并进行选择性抽象的知识。

听起来是不是有点玄奥?系统分析师、产品经理到底要拿出什么样的领域模型才能说“我的工作已经做到位了”?这个模型到底是不是可以实现的?开发人员、测试人员到底有没有理解这个模型?大家的理解是不是一致的?

说到底,一个领域模型要想有用,它必须足够严格。如何使用一种严格的方式描述经过严格组织并进行选择性抽象的知识呢?

问题的答案很自然地指向了DSL。

其实,Eric Evans早就意识到了这一点。他曾经在访[5]谈中说:

更多前沿的话题发生在领域专用语言(DSL)领域,我一直深信DSL会是领域驱动设计发展的下一大步。现在,还没有一个工具可以真正给我们想要的东西。但是人们在这一领域比过去做了更多的实验,这使我对未来充满了希望。

通过本书,我想要告诉大家的是:在项目中运用DDD可以不像大家想象的那么痛苦,DDD并不是只适用于大项目,使用DDD并不一定需要牺牲敏捷性,一切的关键在于DSL的运用。

我和我工作过的团队曾经在多个项目中使用DSL实现了DDD的真正落地。独乐乐不如众乐乐!现在,我想把这些实践经验分享给大家。

读者对象

领域模型是一种“思想”,它可以为软件开发的全过程提供指导,所以我相信本书可以为很多人提供帮助:

·产品经理与系统分析师。产品经理不仅仅需要保证团队开发的是“正确的软件”,也不应该只是关注软件的界面原型、用户体验,更应该让软件有内涵。迷人的产品不仅需要漂亮的界面和交互,更需要逻辑自洽,功能处处传达出一个思想——优美而深刻的领域模型。在有的团队中,产品经理同时也是系统分析师。作为近十几年来最有影响力的软件分析和设计的方法论,系统分析师有必要了解DDD,从中汲取营养。

·架构师。架构师需要根据业务需求提供技术解决方案。DDD想要构建的领域模型不仅仅是领域业务知识的提炼总结,也包含了对软件设计的考量,可以用于直接指导软件的编码实现。构建这样的模型,需要架构师的积极参与。

·开发工程师。开发工程师应该理解领域模型,领域模型应该被忠实地映射到代码实现中。代码中对象的命名、对象之间的关系,都应该与领域模型一致。唯有如此,代码才能具备良好的可读性、可维护性,敏捷XP方法的狂热爱好者们所言的“代码即文档”才可能实现。

·测试工程师。如今很多测试工程师已经被称为“测试开发工程师”,他们也应该理解领域模型。测试工程师应该像产品经理一样了解软件的设计,甚至应该比产品经理更深刻地理解领域模型面向软件设计所做的考量。实例化需求的自动化测试(或者说行为驱动测试)应该基于领域模型,而非基于UI/UE来构建,因为用户界面以及用户交互是易变的,而领域模型相对来说稳定得多。

·项目经理。项目经理是负责“正确开发软件”的人。如果不深刻理解领域,不知道如何抓住领域模型中的关键点,会很难评估任务工作量的大小以及应该在何处投入足够的资源,甚至无法判断项目的实际进度。

·高校研究生以及其他有志于从事IT行业的人。

如何阅读本书

本书的第一部分会带领读者从战术层面以及战略层面重温领域驱动设计的重要概念,然后进一步阐述Eric Evans经典著作中没有显式提出的或者被太多人忽略的但我认为对DDD落地非常重要的若干概念,同时简要介绍从DDD社区兴起的一些软件架构模式。通过第一部分,读者可以更完整、更深刻地掌握DDD的知识体系。

第二部分阐述如何设计一种DDD的DSL,包括这个DSL的规范(Specification)支持哪些特性、如何帮助团队描述领域模型的方方面面、这些特性的选择基于何种考量等。

这种领域专用语言需要一个名字,我们总不能一直说“我设计的DDD的DSL”吧,于是我给它起了一个名字:DDDML。我认为这是一个很棒的名字。其实这种语言叫什么并不太重要,重要的是它可以用一种足够严格的方式描述领域模型。我认为目前它在简单与复杂之间取得了不错的平衡。当然,其中还有不小改进的空间。比如,我很乐意让它支持更多像“账务模式”这样的分析模式。

第三部分介绍如何将“思想照进实现”——通过使用工具将描述领域模型的DSL文档变成可以运行的软件。这个过程涉及大量的技术工具(工具链)的设计与实现。只有将这些技术工具——比如从DSL自动生成应用的源代码的模板——实现出来,才能减轻开发人员实践DDD的负担,进而提升而不是降低软件团队的生产效率。本部分会介绍这些技术工具设计与实现的细节。

我和我的同事把自制的DDDML工具链称为DDDML Tools。出于商业原因,我无法展示这些工具的源代码,但是会详尽地展示这些工具运行的结果——主要是由工具生成的应用的源代码。这些源代码可能经过一些简化,但是与我们在生产系统上运行的代码十分接近,完全可以说明问题。

读完全书,你将发现其实我们已经全无秘密。你会熟知DDDML的规范,见到工具运行的结果,你几乎可以马上动手制造自己的DDDML工具。其实设计DDDML的规范才是整件事情(使用DSL实现领域驱动设计)中最难的部分,制作工具不是。虽然想要复刻我们已经做过的所有工具确实需要相当大的工作量,但也仅仅是工作量而已。

幸运的是,你并不需要制造整条DDDML的工具链才可以享受使用DSL的乐趣。比如,你可以先写一些模板,生成一些持久对象(Persistant Object),它们无非是一些简单的Java对象(Plain Ordinary Java Object,POJO),然后再生成一些O/R Mapping XML,就可以马上使用JPA/Hibernate来实现应用的DAL(数据访问层)了。如果你再生成Repository,那就更漂亮啦!

第四部分讲述的是一些建模案例以及其他与DDD相关的话题。领域模型是一种思想,DSL是一种工具,但是如何运用、结果如何,因人而异。我希望通过轻松的漫谈和随想,将我的一点DDD应用经验分享给大家。

勘误和支持

由于作者水平有限,编写时间仓促,书中难免会出现一些错误或者不准确的地方,恳请读者批评指正。为此,我特意创建了一个微博账号“@领域驱动设计”,不管你遇到什么问题,都可以到该微博账号上咨询,我将尽量在线上为读者提供满意的解答。本书的勘误信息也会在这个微博账号发布。如果你有更多宝贵的意见,欢迎发送邮件至我的邮箱lingyuqudong@163.com,很期待听到你们的真挚反馈。

致谢

首先要感谢Eric Evans,是他将伟大的DDD带到世间。

感谢我的朋友何李石,他在百忙之中抽出时间审阅了本书的第一部分和第二部分,并提出了宝贵的修改意见。感谢我的挚友李永春,他仔细通读了全书,为我挑出了许多错别字、病句以及行文不流畅之处,当然更提了不少内容上的改进建议。

感谢机械工业出版社华章公司的编辑杨绣国老师,感谢她的魄力和远见,并在这一年多的时间中始终支持我写作,是她的鼓励和帮助引导我顺利完成了全部书稿。

特别要感谢我的爱人张丽君,感谢她的陪伴和支持。

杨捷锋

于上海

[1] 见https://github.com/cncf/toc/blob/master/DEFINITION.md。

[2] 见https://developer.aliyun.com/topic/cn-architecture-paper。

[3] 见https://landscape.cncf.io/。

[4] 见https://knative.dev。

[5] Eric Evans on why DDD Matters Today, https://www.infoq.com/articles/eric-evans-ddd-matters-today/。