- 程序设计语言指导怎样把设计更好地落地
- 各种编程范式指导可以用什么样的元素去做设计
- 设计原则与模式指导如何组合分解出来的各个元素
分解组合的东西是从哪来?
需要你对设计方法有一个基本的认知,要理解真实世界中,解决具体问题的过程。
来谈谈设计方法,了解一下设计的基本过程。
1 哪些设计方法
有些人一上来会先设计DB,因为它们觉得,程序=数据+函数:
- 数据呢,就要存到数据库里
- 剩下的就是根据需要对数据库表进行增删改查
这实际上是一种结构化编程的思路。
后来有人就用OO,先找实体,即对象,当然这些实体也要有一些能力。最终,这些对象还是要写到数据库里,同样也是要支持增删改查。
本质上没啥区别,都是围绕数据处理。业务需求不复杂时,围绕数据做文章的做法还能满足开发要求,但软件越来越复杂,这种做法就越发笨拙。 如果我们靠着程序员们本能的做法,就会遇到各种问题,所以,很多人探索了不同做法。
有一种做法逐渐脱颖而出,它成功地解决业务软件开发中遇到的大部分问题,这就是DDD。虽然它不是万能药,但对大部分人面对的场景而言,它都能够有效地应对。
2 DDD
从 Eric Evans 的著作《领域驱动设计》正式出版开始。
这种设计方法通过使用通用语言,让业务人员加入到设计过程中,拉近了业务人员与开发人员之间的距离,打破了组织的藩篱。提供了一套标准建模方法,帮助团队识别业务模型,避免程序员犯下一些低级错误。
然而很多程序员不知道 DDD,一个重要原因是 Eric 这本书写得实在不咋样。要想读出东西,你得比较懂DDD,但大多数人并不懂。
后来,微服务兴起,人们越发认识到,微服务难度不在于将一个系统拆分成若干服务,而是如何有效划分微服务。 这时,人们发现,DDD是最好指导。
学习 DDD,就要从理解 DDD 的根基入手:通用语言(Ubiquitous Language)和模型驱动的设计(Model-Driven Design),而领域驱动设计的过程,就是建立起通用语言和识别模型的过程。
3 通用语言
在业务人员和开发人员之间建立起的一套共有的语言。在从前的设计方法中,业务人员总是把问题扔过墙头,让开发解决:
- 业务说的都是业务名词:产品、订单等
- 开发嘴里都是技术:线程、存储等
二者除了最基础的几个概念之外,其他内容基本无法沟通。人为屏障就存在于开发和业务间。
软件设计是要在问题和解决方案架设一座桥梁,好的设计要更接近问题。开发人员对解决方案一端简直再熟悉不过了,但是对业务一端理解则通常不够充分。而通用语言所做的事情,就是把开发人员的思考起点拉到了业务上,也就是从问题出发,这就在一定程度上填平了那道人为的鸿沟。
通用语言是什么
就是这个业务中有哪些概念以及哪些操作。
如做电商平台,就要有产品、订单的概念。其中,产品就要有上架、下架、修改产品信息等操作,而订单就会有下单、撤单、修改订单等操作。
在业务人员看来,这里说的都是自己擅长的事情,自己就可以有更多的发言权。在开发人员的视角,概念就是一个一个的类,操作就是一个一个的方法,也很好理解。有一套通用语言,皆大欢喜。
通用语言从哪来
即如何设计通用语言呢?最简单的,让业务人员和开发人员一起,找一块白板,把各种概念都写在上面。然后,双方重新进行分类整理。
这里面的重点是,让业务人员和开发人员在一起。如果只让一方出现,结果又会是原来的样子,因为你没法判断,这里面的语言对方是否听得懂。这种做法很简单,但通常都不够系统,会存在各种遗漏。所以,有人探索出一种更正式的实践:事件风暴(Event Stroming)。
4 事件风暴
一个工作坊,找一面很宽的墙,上面铺上大白纸,然后,用便利贴把识别出来的概念贴在上面。当然,前提依然是让业务人员和技术人员都参与。
事件风暴的关注点在于领域事件。领域事件是用来记录业务过程中发生过的重要事情,比如,作为电商平台的工作人员,你想知道产品是不是已经上架了,这个领域事件就是产品已上架;作为消费者,你会关心我的订单是不是下成功了,这个领域事件就是订单已下。
人们做了一个动作,都会关心做过这个动作之后的结果,所以,领域事件用的描述方式都是过去式,比如:OrderPlaced。
4.1 事件风暴步骤
① 把领域事件识别出来
这个系统有哪些是人们关心的结果。有了领域事件,下面一个问题是,这些事件是如何产生的,它必然会是某个动作的结果。
② 找出这些动作
即引发领域事件的命令。如:
- 产品已上架,由产品上架这个动作引发
- 订单已下,就由下单这个命令引发的
③ 找出与事件和命令相关的实体或聚合
如,产品上架就需要有个产品(Product),下单就需要有订单(Order)。
至此,已将最核心的内容找出来了。工作坊过程中,为增强趣味性和清晰性,不同的概念会用不同的颜色的便利贴标识出来,比如,领域事件用橙色、命令用蓝色、实体/聚合用黄色等等。
用不同的颜色建模,事件风暴并不是独一份。Peter Coad也曾提出四色建模:
- 粉色表示时标性对象(moment-interval)
- 黄色表示角色(role)
- 蓝色表示描述(description)
- 绿色表示人、地点、物(party/place/thing)。
5 模型
模型是对领域的抽象和模拟:
建模是针对特定问题建立领域的合理模型:
6 模型驱动设计
有了通用语言,接下来就进入模型设计阶段了。虽然有了通用语言,但是业务人员能够帮到开发人员的还是很少,他们只能告诉开发人员哪些模型是符合业务概念的。
但这么多的业务模型,该如何组织呢?怎样补全欠缺的模型,使之成为一个可以落地的方案呢?这就是开发人员要想办法解决的事情了。
也正是因为在通常情况下,业务模型数量众多,所以在 DDD 的过程中,我们将设计分成了两个阶段:
战略设计(Strategic Design)
高层设计,将系统拆分成不同领域。而DDD核心概念就是领域,即它给了我们一个拆分系统的新视角:按业务领域。如将电商系统拆分成产品域、订单域、支付域、物流域等。拆分后,识别出来的各种业务对象就会归结到各领域。
然而,有时不同领域的业务对象会进行交互,比如想知道订单的物流情况。所以,要在不同领域之间设计一些交互方式。
战术设计(Tactical Design)
低层设计:如何具体组织不同的业务模型。该层次上,DDD 提供了一些标准做法。如哪种模型应该设计成实体,哪些设计成值对象。
还要考虑模型之间的关系,比如,哪些模型要一起使用,可以成为一个聚合。接下来,我们还需要考虑这些模型从哪来、怎样演变,DDD 同样为我们提供了一些标准的设计概念,比如仓库、服务等等。
7 系统复杂性的来源
7.1 业务复杂导致模型复杂
若同时考虑三个业务呢?就会变得很复杂:
7.2 技术实现引入额外复杂性
团队 A 负责数据接入,开发接口简单直接,商品和门店数据完全解耦。
团队 B 负责商品服务,开发商品搜索时,可能要支持对地理信息、门店的搜索,如优先搜索附近门店的商品,那就需要结合商品、门店的数据。
为提高搜索的实时性,AB 商讨在 A 构建数据时,为 B 的商品搜索服务再构建一份门店商品数据。于是就导致 A 系统变化:
导致 A 系统为此,还得考虑额外的复杂性。
因此,传统软件设计无法解决这些问题,但是 DDD 可以:
9 DDD 的核心思想
9.1 模型分解
将复杂的问题,划分成多个相对简单的模型,让不同模型去解决不同问题。
领域划分
限界上下文
9.2 模型驱动设计
通过分层架构隔离领域层、仔细选择模型和设计方案等措施保持实现与模型的一致。所以,对之前案例,应将搜索子领域单独提出来,从商品业务领域获取数据,当然了,中间也要经过防腐层(转换层)。于是,就达到了模型驱动设计。
8 有助于解决系统老化问题
新需求越来越难
我又不贷你这系统具体怎么实现的系统越来越复杂,需求要怎么提?
开发越来越难
一个类上千行代码,这怎么看?这段代码有什么用? 能不能去掉?
技术创新越来越难
外面新技术越来越多,我们这个老系统没时间重构,越拖越烂。
测试越来越难
没办法单元测试,一个小需求又要回归测试。累死了。
总结
理解DDD就要理解通用语言和模型驱动设计。通用语言就是要把业务人员和开发人员对具体业务概念和逻辑的理解达成一致,可使用事件风暴和彩色建模等方法建立通用语言 模型驱动设计可以从战略设计和战术设计两方面入手,战略设计属于高层设计,将系统安装领域拆分;战术设计属于低层设计,考虑的是如何组合业务模型 建立一套业务人员和开发人员共享的通用语言。