gitbook/软件设计之美/docs/266819.md
2022-09-03 22:05:03 +08:00

115 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 27 | 领域驱动设计:如何从零开始设计一个软件?
你好!我是郑晔。
在前面的内容中,我给你讲了软件设计的各种基本工具。我们学习了程序设计语言,知道怎样把设计更好地落地;学会了各种编程范式,知道了可以用什么样的元素去做设计;我们还学习了设计原则与模式,知道了怎样组合分解出来的各个元素。
工具都有了,理论也武装上了,那么我们该如何实践呢?或者说,我要去分解组合的东西是从哪而来的呢?这就需要你对**设计方法**有一个基本的认知了,也就是说,我们要理解,在真实世界中,解决具体问题是怎样的一个过程。
那从这一讲开始,我们就来谈谈设计方法的话题,一起了解一下设计的基本过程。
首先,你知道哪些设计方法呢?
我知道的一种做法是,有些人一上来会先设计数据库,因为他们觉得,程序就是数据加函数。数据呢,就要存到数据库里,剩下的就是根据需要对数据库表进行增删改查。但是从我们之前的讲解中,你可以看出,这种思路实际上是一种结构化编程的思路。
后来有人就用面向对象的思路,先来找实体,也就是对象,当然这些实体也要有一些能力。最终,这些对象还是要写到数据库里,同样也是要提供增删改查的能力。
你看,这两种做法本质上没什么太大的区别,都是**围绕着数据在做文章**。在业务需求不复杂的年代,围绕数据做文章的做法还能满足开发的要求,但随着软件日益深入到人们日常工作和生活中,软件变得越来越复杂,这种做法就越发显得笨拙了。
对,软件会越来越复杂的。当软件变得复杂起来,如果我们靠着程序员们本能的做法,就会遇到各种问题,所以,很多人探索了不同的做法。
在诸多的探索之中,有一种做法逐渐脱颖而出,它成功地解决业务软件开发中遇到的大部分问题,这就是**领域驱动设计**。虽然它不是万能药,但对大部分人面对的场景而言,它都能够有效地应对。
## 领域驱动设计
领域驱动设计Domain Driven Design简称 DDD作为一个新的设计方法正式登上历史舞台是从 Eric Evans 的著作《领域驱动设计》正式出版开始的。
这种设计方法通过使用通用语言,让业务人员加入到设计过程中,拉近了业务人员与开发人员之间的距离,打破了组织的藩篱。同时,还提供了一套标准的建模方法,帮助团队识别业务模型,避免程序员犯下一些低级错误。
按理说,这种设计方法这么好,应该很快流行起来才对。然而真实情况是,很多程序员都不知道 DDD一个重要的原因就是 Eric 的这本书写得实在不怎么样。要想从中读出味道你得比较懂DDD但是大多数人并不懂这就是矛盾的地方。所以DDD在很长一段时间都被埋没了。
不过,后来,随着**微服务**的兴起人们越发认识到微服务的难度并不在于将一个系统拆分成若干的服务而在于如何有效地划分微服务。这个时候人们发现DDD才是最恰当的指引。关于微服务和 DDD 之间的关系,我在[《10x 程序员工作法》](https://time.geekbang.org/column/intro/100022301)里已经讲过了,有兴趣的可以去了解一下,这里就不再赘述了。
现在你已经知道了DDD的好准备学习DDD 了。但你只要一打开 DDD 的书一大堆名词就会扑面而来限界上下文、聚合根、实体、值对象等等。我该如何下手学习呢这是摆在每个DDD学习者面前最严峻的问题。
**学习 DDD就要从理解 DDD 的根基入手通用语言Ubiquitous Language和模型驱动的设计Model-Driven Design**,而领域驱动设计的过程,就是建立起通用语言和识别模型的过程。
## 通用语言
**通用语言,就是在业务人员和开发人员之间建立起的一套共有的语言**。要知道,在从前的设计方法中,业务人员总是把问题扔过墙头,让开发人员去解决。可是,业务人员说的都是业务名词,比如:产品、订单等等,而开发人员嘴里全是技术,比如:线程、存储等等,二者除了最基础的几个概念之外,其他的内容基本是没法沟通的。所以,一道人为鸿沟就在开发人员和业务人员之间形成了。
我们在[第1讲](https://time.geekbang.org/column/article/240177)说过,**软件设计是要在问题和解决方案架设一座桥梁,好的设计要更接近问题**。开发人员对解决方案一端简直再熟悉不过了,但是对业务一端理解则通常不够充分。而通用语言所做的事情,就是把开发人员的思考起点拉到了业务上,也就是从问题出发,这就在一定程度上填平了那道人为的鸿沟。
通用语言是什么呢?就是这个业务中有哪些概念以及哪些操作。比如说,我要做一个电商平台,就要有产品、订单的概念。其中,产品就要有上架、下架、修改产品信息等操作,而订单就会有下单、撤单、修改订单等操作。
在业务人员看来,这里说的都是自己擅长的事情,自己就可以有更多的发言权。在开发人员的视角,概念就是一个一个的类,操作就是一个一个的方法,也很好理解。所以,有一套通用语言,双方皆大欢喜。
但是,通用语言是从哪来的呢?也就是说,如何设计通用语言呢?最简单的做法就是让业务人员和开发人员一起,找一块白板,把各种概念都写在上面。然后,双方重新进行分类整理。
这里面的重点是,让业务人员和开发人员在一起。如果只让一方出现,结果又会是原来的样子,因为你没法判断,这里面的语言对方是否听得懂。
这种做法很简单,但通常都不够系统,会存在各种遗漏。所以,有人探索出一种更正式的实践:**事件风暴**Event Storming
[事件风暴](https://www.eventstorming.com/)是一个工作坊,基本做法就是找一面很宽的墙,上面铺上大白纸,然后,用便利贴把识别出来的概念贴在上面。当然,前提依然是让业务人员和技术人员都参与其中。
这个实践之所以叫作事件风暴,因为它的关注点在于**领域事件**。领域事件是用来记录业务过程中发生过的重要事情,比如,作为电商平台的工作人员,你想知道产品是不是已经上架了,这个领域事件就是产品已上架;作为消费者,你会关心我的订单是不是下成功了,这个领域事件就是订单已下。
人们做了一个动作都会关心做过这个动作之后的结果所以领域事件用的描述方式都是过去式比如OrderPlaced。
事件风暴这个工作坊主要分成三步:
* **第一步就是把领域事件识别出来**,这个系统有哪些是人们关心的结果。有了领域事件,下面一个问题是,这些事件是如何产生的,它必然会是某个动作的结果。
* **第二步就是找出这些动作,也就是引发领域事件的命令**。比如:产品已上架是由产品上架这个动作引发的,而订单已下就是由下单这个命令引发的。
* **第三步就是找出与事件和命令相关的实体或聚合**比如产品上架就需要有个产品Product下单就需要有订单Order
至此,我们已经把最核心的内容找出来了。通常,在工作坊过程中,为了增强趣味性和清晰性,不同的概念会用不同的颜色的便利贴标识出来,比如,领域事件用橙色、命令用蓝色、实体/聚合用黄色等等。
其实用不同的颜色建模事件风暴并不是独一份。Peter Coad也曾提出过一种[四色建模](https://en.wikipedia.org/wiki/Object_Modeling_in_Color)的方法:
* 粉色表示时标性对象moment-interval
* 黄色表示角色role
* 蓝色表示描述description
* 绿色表示人、地点、物party/place/thing
他还写了一本[《彩色UML建模》](https://book.douban.com/subject/3354137/)Java Modeling in Color with UML介绍这种方法。我在ThoughtWorks的前同事徐昊按照自己的理解对这种方法做了一些更新有兴趣的话可以去[了解一下](https://www.infoq.cn/article/xh-four-color-modeling)。
当然,这里的事件风暴,我只是描述了最简单的一个过程。在具体实施的过程中,还会有更多的细节。不过,最重要的还是,让不同角色的参与其中,让知识在所有人的头脑中进行构建,得到一个大家都认同的结果。
## 模型驱动设计
有了通用语言,接下来就进入模型设计阶段了。虽然有了通用语言,但是业务人员能够帮到开发人员的还是很少,他们只能告诉开发人员哪些模型是符合业务概念的。
但这么多的业务模型,该如何组织呢?怎样补全欠缺的模型,使之成为一个可以落地的方案呢?这就是开发人员要想办法解决的事情了。
也正是因为在通常情况下,业务模型数量众多,所以在 DDD 的过程中,我们将设计分成了两个阶段:**战略设计**Strategic Design和**战术设计**Tactical Design
战略设计是高层设计,是指将系统拆分成不同的领域。而领域驱动设计,核心的概念就是领域,也就是说,它给了我们一个拆分系统的新视角:按业务领域拆分。
比如,我把一个电商系统拆分成产品域、订单域、支付域、物流域等。拆分成领域之后,我们识别出来的各种业务对象就会归结到各个领域之中。然而,有时候,不同领域的业务对象会进行交互,比如,我要知道自己订单的物流情况。所以,要在不同的领域之间设计一些交互的方式。
而战术设计是低层设计也就是如何具体地组织不同的业务模型。在这个层次上DDD 给我们提供了一些标准的做法供我们参考。比如,哪种模型应该设计成实体,哪些应该设计成值对象。
我们还要考虑模型之间是什么样的关系比如哪些模型要一起使用可以成为一个聚合。接下来我们还需要考虑这些模型从哪来、怎样演变DDD 同样为我们提供了一些标准的设计概念,比如仓库、服务等等。
通过战略设计和战术设计,我们就可以把发现出来的不同业务概念各归其位了。
## 总结时刻
今天,我们讲了领域驱动设计,这是目前在软件行业内最符合软件发展趋势的一种设计方法,因为它把软件设计的起始点从技术拉到了业务。
学习领域驱动设计,我们要从通用语言和模型驱动设计入手。通用语言是在业务人员和技术人员之间建立一套共有的语言,开发通用语言的一种实践是事件风暴,这是一种工作坊,通过识别领域事件找到引发事件的命令,找出与事件和命令相关的实体或聚合,帮助团队建立通用语言。
DDD 的模型设计可以分为战略设计和战术设计。战略设计是高层设计,将系统拆分成领域,战术设计是低层设计,考虑如何组织不同的模型。
好,我们已经对 DDD 有了一个初步的了解。接下来的两讲,我们就分别来看看,如何进行战略设计和战术设计。
如果今天的内容你只能记住一件事,那请记住:**建立一套业务人员和开发人员共享的通用语言**。
![](https://static001.geekbang.org/resource/image/88/03/88d3dd98642fd2d2d619a435d0d85903.jpg?wh=2284*1700)
## 思考题
最后,我想请你分享一下,你们在实际工作中是如何与业务人员沟通的?欢迎在留言区写下你的想法。
感谢阅读,如果你觉得这一讲的内容对你有帮助的话,也欢迎把它分享给你的朋友。