怼不过产品经理?因为你不懂DDD领域建模与架构设计
那DDD到底是什么呢?有什么技术落地方案呢?今天我来给大家科普一下。
基本概念
过去系统分析和系统设计都是分离的,正如我们国家“系统分析师” 和“系统设计师” 两种职称考试一样,这样割裂的结果导致,需求分析的结果无法直接进行设计编程,而能够进行编程运行的代码却扭曲需求,导致客户运行软件后才发现很多功能不是自己想要的,而且软件不能快速跟随需求变化。
DDD则打破了这种隔阂,提出了领域模型概念,统一了分析和设计编程,使得软件能够更灵活快速跟随需求变化。
DDD专门为解决复杂性而诞生,因此解决思路完全不同于传统的CRUD,但是DDD本身掌握起来并不会感觉复杂,从程序员角度看,DDD其实是研究将包含业务逻辑的ifelse语句放在哪里的学问。
Q:DDD适用于什么场景?
A:在Evans写的《领域驱动设计》一书,副标题已经说明一切:软件核心复杂性应对之道。领域驱动的真正作用,在于项目中对日后的维护,当系统变得越来越复杂的时候,才能体现出它的威力。
那新项目该不该用领域驱动?新项目并不复杂,产品还在跑模式的阶段,虽然它的优势并不能真正发挥出来,但我认为,DDD同样适用新项目。项目只会越做越复杂,我们从一开始就应该考虑日渐发胖的代码、随时可能独立的子业务。而新项目更多的是指导战略层设计(如领域建模),战术层的技术落地还是以团队成员最熟悉的方式进行,目标是持续快速交付、降低维护成本。
领域建模
Q:什么是领域模型?
A:为解决场景下的问题而形成的一套模型,然后使用这套模型来解决业务问题。 根据重复劳动经验我们会形成一套模式,领域模型也一样会形成一套模式,包括:实体、值对象、模块、领域服务。
那领域模型是怎么一步一步确定下来的呢?推荐两种比较常用的领域发现方法:事件风暴与四色建模法。
一、事件风暴
Event Storming(事件风暴)是一种轻量级的系统分析方法,基于 DDD 的概念,能够为我们梳理系统中的各种相关元素,其中包括了核心的 Aggregate。它能够帮助开发人员梳理核心的业务对象,从某种程度上来说就是就是领域对象中的聚合。
描述产品愿景
产品愿景的主要目的是对产品顶层价值的设计,使产品目标用户、核心价值、差异化竞争点等信息达成一致,避免产品偏离方向。
产品愿景的参与角色:领域专家、业务需求方、产品经理、项目经理和开发经理。
事件风暴关注点
属性:事件的输入、输出
命令:某个动作
实体:命令的触发者
二、四色建模法
通过还原业务逻辑事件,依据是否影响公司的运营和发展,确定凭证作为时标型对象,并补全相关描述的建模方法。四色建模法一般运用于问题分析建模。
四色建模是哪四色?它包括——
时标型(Moment-Interval)对象:具有可追溯性的记录运营或管理数据的时刻或时段对象,用粉红色表示;
PPT(Party/Place/Thing)对象:代表参与到流程中的参与方/地点/物,用绿色表示;
角色(Role)对象:在时标型对象与 PPT 对象(通常是参与方)之间参与的角色,用黄色表示;
描述(Description)对象:对 PPT 对象的一种补充描述,用蓝色表示。
找到溯源事件
确定时标型对象
找到周围的PPT对象
找到角色
补全描述对象
对于不同的人提炼出来的领域模型不可能完全一致,这是因为每个人对业务理解的角度都不同。那么,怎么才能保证建模的正确性?
我给大家梳理以下几个步骤:
一、统一语言,梳理业务
在做设计的时候,梳理业务贯穿了整个过程。这需要技术与业务专家利用统一语言,描绘需求或问题本身,不断梳理业务,提炼出核心的领域模型(而非表设计)。这有利于拉近技术人员与业务人员之间的关系,建立信任,达成统一的业务目标。
二、识别聚合、聚合根
如何识别聚合与聚合根?
一个Bounded Context(限界上下文)可能包含多个聚合,每个聚合都有一个根实体,叫做聚合根;
聚合是用来封装真正的不变性,而不是简单的将对象组合在一起 聚合应尽量设计的小,尽可能小的拆分,可以避免重构,重新拆分
聚合之间的关联通过ID,而不是对象引用
聚合内强一致性,聚合之间最终一致性
三、划分限界上下文
第二步完成以后,我们根据不同的场景来划分限界上下文,以便进行微服务拆分。通过这种基于业务理解的拆分方式,我们的系统就能做到高内聚,做到单一职责,拆分出来的每个微服务都是软件变化的一个原因,不会因为某个原因发生的变更去修改每个微服务,“牵一发而动全身”。
架构设计
架构设计作为DDD战术层面的设计,关乎到设计如何落地到项目中。下面介绍跨库关联查询解决方案及几种比较流行的架构设计方案。
方案一:数据冗余
方案二:数据补填
结合Wrapper设计模式,一般在Dao层实现数据聚合——本地库分页查询完数据后,通过查询条件判断是否需要填充关联数据,若需要则通过跨服务查询相关联的服务,再对各个服务的数据进行填充组装,最后返回。
如下图,要实现患者预约查询,并聚合患者、医生数据,则在患者预约服务查询完预约表数据后补填患者服务和医生服务的数据。
这种方法的缺点就是,当一个完整的数据涉及到N个微服务,就会增加N-1个服务调用,数据全量查询/导出的场景也不好使。
CQRS(Command Query Responsibility Segregation)指的是命令查询职责分离。Command服务专门写数据,使用关系型数据库以保证ACID;Query服务专门读数据,一般使用NoSQL数据库,实现宽表查询,如MongoDB、ElasticSearch等。这是一种索引外置方案。
Event Sourcing事件溯源——简单来说就是,通过事件来管理领域对象的生命周期,事件即领域对象已发生的事实,只增不改。一个对象从创建开始到消亡会经历很多事件,以前我们是在每次对象参与完一个业务动作后把对象的最新状态持久化保存到数据库中,也就是说我们的数据库中的数据是反映了对象的当前最新的状态。
下图为一种支持读写分离的演化:
整洁架构的核心思想是通过适配器层解耦业务层与技术框架层代码,使得业务代码与技术框架可以各自升级迭代,互不影响。我们都知道,技术架构一直以来都在不断变化,对项目的技术架构调整成本是非常高的,如何降低这种成本?这时候整洁架构就派上用场了。
如上图所示,中间的Entities与Use Cases属于业务领域层,Entities表示业务领域模型的核心业务,Use Cases表示与用户交互的Service;最外层技术框架层是各种技术实现,与业务无关的一层;那业务与技术怎么进行关联呢?通过中间绿色的接口适配器层实现。适配器层分离了技术实现与业务逻辑。
六边形架构是微服务设计的基础。
如图,我们把微服务封装在六边形里,每个微服务的核心业务是六边形里的应用程序与领域模型。与整洁架构类似,外部接口与内部应用层通过各种适配器进行关联解耦,当发生变更的时候,只需要修改六边形内部即可,不需要修改其它微服务。
清晰架构融合了DDD、整洁架构、CQRS……曾在高水准的平台生产代码中应用,其中一个是拥有数千家遍布全球的网上商店的 SaaS 电子商务平台,另一个是已经在两个国家上线的市场,拥有可以每月处理超过两千万条消息的消息总线。
以上给大家介绍了DDD的基本概念、领域建模及几种主流的架构设计方案。DDD是一个非常大的课题,工程师们对DDD的各种争论从不休止,但存在即合理,我们并不一定要把DDD落地到项目中去,在战略层设计可以指导我们准确梳理业务。
无论如何,将来想要成为业务架构师,DDD领域建模与架构设计是一堂必修课,参与到这场思想运动与实践中是非常有必要的。
End
回复「题库」领取海量算法题库
回复「设计」学习UML设计技巧