领域驱动设计(DDD)中简单易用的10种技巧

d_hero
发布于 2022-9-14 10:52
浏览
0收藏

领域驱动设计(DDD),因非常适合与微服务进行配合而闻名,因《领域驱动设计》那本书的难懂而让人望而却步。

其实《领域驱动设计》这本书讲的是:以领域为核心,在代码中体现领域的思想,开发人员和领域专家要紧密沟通。这也侧面给出了公司划分组织结构的建议。

开始之前,我要声明一点:领域驱动设计不是万能药,它适合于实时系统,而对于分析汇总的场景,则不必使用领域驱动设计。

技术是在进步的,现在大家对领域驱动设计抽象的越来越好。今天就来总结一下领域驱动设计简单易用的技巧。首先声明一下:提到的技巧不一定是领域驱动设计提出来的,但是这些技巧可以帮助我们更好的进行领域驱动设计。

 

限界上下文

画架构图尽量简单易懂。但是项目本身很复杂怎么办呢?那就要按问题域划分子域。每一个子领域架构是很简单的,想看到全貌可以将它们之间的关联形成上下文地图。这就是限界上下文的作用。

领域驱动设计(DDD)中简单易用的10种技巧-鸿蒙开发者社区

DDD建议区分场景来划分子领域,通过依赖接口而不是具体实现解耦。使用微服务实现限界上下文中的低耦合。微服务不是简单的拆分,而是对设计提出了更高的要求。

举个常用的例子:SOA架构中建议进行数据库操作单独提供服务进行,使用方通过接口依赖实现低耦合。这个思想和DDD的思想异曲同工:DDD建议将领域对象与数据库映射,把数据表的增删改查采用接口调用实现。底层数据表结构的修改对上层屏蔽。这就是将软件对象进行持久化操作,并且形成独立的限界上下文。DDD还要求数据库表设计以领域模型为核心,并且推荐不要在sql中进行join操作,而是通过逻辑层来进行,让表与表之间的关系直接转化为软件对象之间的关系。

如果看到这里你还是觉得限界上下文很抽象,不用担心,其他的技巧就是来帮忙咱们划分独立的子领域。

 

聚合与聚合根

聚合体现的是整体与部分的关系。部分与整体有相同的生命周期。一旦将对象间的关系设计成聚合,外部对象只能访问“聚合根”。充血模型就是一种聚合的实现。

领域驱动设计(DDD)中简单易用的10种技巧-鸿蒙开发者社区

我在《CURD系统怎么做出技术含量--怎样引导面试》里用实例介绍过充血模型。白话来说,就是一个普通的POJO,如:“人”对象,里面有对应属性,如姓、名、年龄、学历、出生年月。贫血模型中“人”对象只包含可以通过lombok自动生成的方法。而充血模型则可以包含由各个属性组成的一个完整简历的方法,或者是直接返回星座的方法。外部只能通过这些方法,也就是“聚合根”来操作“人”这个对象,不能直接访问其内部属性。因为部分和整体有相同的生命周期,意味着人这个对象被销毁,它的属性和方法也同时消失。

 

DDD工厂与装载

DDD工厂与设计模式中的工厂有较大的差别。

设计模式中的工厂,是将被调用方设计成一个接口下的多个实现,并装配到工厂中。工厂负责通过key值找到对应的实现类,创建出来,返回给调用方。这样就降低了调用方与被调用方的耦合度。

DDD工厂是通过工厂创建领域对象,作为对象生命周期的起点。它的核心在于装配这个环节,比如订单工厂需要将订单对象是要将订单概述、订单明细统一封装到订单对象中。

举个例子:漫画中的超级英雄在设计模式工厂中,工厂创建出来的时候可能是个小菜鸟,在业务逻辑运行过程中,他可能会需要升级打怪才变成超级英雄。而在DDD工厂中,工厂会负责给超级英雄披上钢铁侠的盔甲,装配好美国队长的盾牌。他从工厂里出来就可以拯救世界了。

 

DDD仓库

DDD工厂和DDD仓库都是聚合的实现。通过DDD工厂装配好的领域对象会返回到DDD仓库中。事实上Spring上下文就是一个DDD仓库底座。它包含了程序需要的各种领域对象。有些领域对象如DAO是从数据库中取的,有些是从缓存中取的,还有些如POJO是直接创建的。但是业务逻辑不关心领域对象的来源,只需要在需要的时候直接使用,就与领域对象的具体实现之间做了解耦。

领域驱动设计(DDD)中简单易用的10种技巧-鸿蒙开发者社区

事件风暴法

在领域设计之初的需求分析阶段,需求分析的基本思路就是统一语言建模。它是我们的指导思想。但落实到实践层面可以采用的方法是事件风暴法。它是一种基于工作坊的DDD实践方法,可以帮助我们发现业务领域中正在发生的事件,指导领域建模以及程序开发。

事件风暴中的风暴是头脑风暴的意思,我在《复联4里用到的方法论》中详细解释过头脑风暴法,这里不再赘述。事件即事实,已经发生,不会更改。信息管理系统可以将这些事实以信息的形式存储,即信息就是事实。信息管理系统的作用就是将信息存储,管理与跟踪。对事件可以采用上游发布、下游订阅的方式进行解耦,按照业务流程来梳理领域事件。

举个例子:把大象装冰箱分成三步,每个步骤都是一个事件,每个事件之间要解耦。打开冰箱门涉及冰箱领域对象、人领域对象等。这个事件完成将会触发一个信息存储:冰箱状态变成已打开。这些都是围绕着打开冰箱门这个事件展开。这个事件完成后,只要对第二步把大象装冰箱进行通知即可。之后的事情再不会和第一步发生关系。

 

防腐层

防腐层(Anti-corruption layer,简称 ACL)介于新逻辑和旧逻辑之间,用于确保新逻辑的设计不受老逻辑的限制。是一种在不同逻辑间转换的机制。

创建一个防腐层,以根据客户端自己的域模型为客户提供功能。该层通过其现有接口与新逻辑进行通信,几乎不需要对其进行任何修改。因此,防腐层隔离不仅是为了保护你的系统免受异常代码的侵害,还在于分离不同的域并确保它们在将来保持分离。

举个例子:我之前带过的项目,由于架构不能满足业务增长的需求,需要进行大范围重构。这里我们使用了“留壳扣瓤”的方法,对外接口保持不变,内部实现换成新的,这个接口层就是我们的防腐层。当然啦,不要以为瓤随随便便就可以换了,过渡期我们保留了旧逻辑,用开关的方式在新老逻辑之间做灰度和切换,并准备好随时降级。

 

谦卑对象模式

谦卑对象模式是整洁架构中提出的一个概念。最初的设计目的是帮助单元测试的编写者区分容易测试的行为与难以测试的行为。难以测试的行为即谦卑对象,要尽量的简单,因为这些难以通过测试保证其正确性。

例如:GUI是很难进行单元测试的。可以利用谦卑对象模式将GUI的这两种行为拆分成视图和数据两部分。视图部分属于难以测试的谦卑对象,这种对象的代码应该越简单越好。

再比如:数据库网关接口的实现,属于谦卑对象。所以我们使用成熟的框架,自身的代码尽量简单。测试时我们只需要对网关接口进行Mock,测试其他逻辑。网关究竟是怎么与数据库进行交互的,往往不在单元测试中考虑。

谦卑对象模式与DDD有什么关系呢?近年来,高可用、稳定性、弹力设计、容错设计和业务连续性越来越得到公司的重视。基于稳定性的隔离术也逐渐成为划分问题域的考虑方向。我在《服务的容灾与容错》这篇文章中总结了隔离术,这里不再赘述。

 

总结

DDD本质就是利用分治的思想将复杂的事情简单化,本文所讲的技巧就是按什么来分治的问题。本文涉及的概念整体结构如下图:

领域驱动设计(DDD)中简单易用的10种技巧-鸿蒙开发者社区

 

文章转载自公众号:编程一生

 

分类
标签
已于2022-9-14 11:00:28修改
1
收藏
回复
举报
回复
    相关推荐