前言

Github:https://github.com/HealerJean

博客:http://blog.healerjean.com

一、界限上下文

1、界限上下午的定义

要明确限界上下文的定义,需要从“限界”与“上下文”这两个词的含义来理解。

上下文表现了业务流程的场景片段,整个业务流程由诸多具有时序的活动组成,随着流程的进行,不同的活动需要不同的角色参与,并导致上下文因为某个活动的执行发生切换,形成了场景的边界。因而,上下文其实是动态的业务流程被边界静态切分的产物

“每个限界上下文提供了不同的业务能力,以满足当前上下文中各个角色的目标。这些角色只会执行满足当前限界上下文业务能力的活动,因为限界上下文划定了领域知识的边界”

image-20240401160725503

“封装了领域知识的领域对象组成了领域模型,在知识语境的界定下,不同的领域对象扮演不同的角色,执行不同的业务活动,并与限界上下文内的其他非领域模型对象一起,对外提供完整的业务能力”

image-20240401161030423

2、界限上下文特征

1)领域模型的知识语境

什么是限界上下文(Bounded Context )?让我们来读一个句子 wǒ yǒu kuài dì

不同的人会理解不同的意思,如“我有快递”,“我有块地”,我们能确定到底是哪个意思呢?确定不了,所以我们必须结合说话人的语气和语境来理解来理解。例如 我有快递,是朋友邮寄的。 我有块地,是我家祖辈留下的。 在日常的对话中,说话的语气与语境就是帮助我们理解对话含义的上下文(Context)现实中的这个语义环境对应到 DDD 里面就是限界上线文

2)业务能力的纵向切分

模块:先从技术维度进行横向切分,再从领域维度针对领域层进行纵向切分。业务模块仅包含业务逻辑,需要其他层模块的支持才能提供完整的业务能力。这样的架构没有将业务架构、应用架构、数据架构绑定起来,一旦业务发生变化,就会影响到横向层次的各个模块。

限界上下文:先从领域维度进行纵向切分,再从技术维度对限界上下文进行横向切分,因此限界上下文是一个对外暴露业务能力的架构整体。无论是业务架构、应用架构,还是数据架构,都在一个边界中,一旦业务发生变化,只会影响到与该业务相关的限界上下文。(限界上下文是领域驱动设计战略层面最重要、最基本的架构设计单元)

模块的划分:

image-20240401162322651

纵向划分的界限上下文:

image-20240401162303388

3)自治的架构单元

限界上下文作为基本的架构设计单元(“限界上下文自身又可视为一个小型的应用系统)既要体现领域模型的知识语境,又要能独立提供业务能力。这就要求它具有自治性,形成自治的架构单元。

最小完备:是实现限界上下文自治的基本条件”,“完备”,是指限界上下文在履行属于自己的业务能力时,拥有的领域知识是完整的,无须针对自己的信息去求助别的限界上下文,这就避免了不必要的领域模型依赖”

自我履行:意味着由限界上下文自己决定要做什么

稳定空间:要求限界上下文必须防止和减少外部变化带来的影响

独立进化:指减少限界上下文内部变化对外界产生的影响。这体现了边界的控制力,对外公开稳定的接口,而将内部领域模型的变化封装在限界上下文的内部

限界上下文自治的4个要素相辅相成。最小完备是基础,只有赋予了限界上下文足够的知识,才能保证它的自我履行。稳定空间对内,独立进化对外,二者都是对变化的有效应对,而它们又通过最小完备和自我履行来保证限界上下文受到变化的影响最小”

image-20240401162830672

3、界限上下文的识别

1)业务维度

领域维度对限界上下文的识别,其实就是在问题空间获得业务服务,然后针对业务服务根据业务相关性对它们进行归类和归纳,获得业务主体,进而根据高内聚低耦合原则以及限界上下文的本质,进一步对业务主体的边界进行调整,获得初步的限界上下文。

从问题空间分析获得业务服务是一个自上而下的过程,对业务服务进行归类和归纳则是一个自下而上的过程,合起来,恰好形成一个V 形,故而,我将这一识别限界上下文的过程称为“V形映射过程”:

image-20240401170624143

2)验证原则

a、正交原则

正交性要求:“如果两个或更多事物中的一个发生变化,不会影响其他事物,这些事物就是正交的。

变化的影响主要体现在变化的传递性,即一个事物的变化会传递到另一个事物引起它的变化,但这个变化影响并不包含彼此正交的点。例如,限界上下文之间存在调用关系,当被调用的限界上下文公开的接口发生变化,自然会影响调用方。这一影响是合理的,也是软件设计很难避免的依赖。故而限界上下文存在正交性,指的是各自边界封装的业务知识不存在变化的传递性

要破除变化的传递性,就要保证每个限界上下文对外提供的业务能力不能出现雷同,这就需要保证为完成该业务能力需要的领域知识不能出现交叉;要让领域知识不能出现交叉,就要保证封装了领域知识的领域模型不能出现重叠。业务能力、领域知识、领域模型,三者之间存在层次的递进关系,无论是自顶向下去推演,还是自底向上来概括,都不允许同一层次之间存在非正交的事物,如图所示。

image-20240401171253992

违背正交性

1、领域模型违背了正交性,意味着各自定义的领域模型对象代表的领域概念出现了重复。例如,在供应链系统中,商品限界上下文、运输限界上下文与库存限界上下文的领域模型都定义了 Product 类,但结合各自的知识语境,这一领域模型类实际代表了不同的领域概念;在保险系统,车险限界上下文、寿险限界上下文的领域模型都定义了 Customer 类,关注的客户属性也是近似的,属于相同的领域概念,导致领域模型的重复。

2、领域知识违背了正交性,代表了业务问题的解决方案出现了重复,通常包含了领域行为与业务规则,例如在电商系统中,运费计算的规则不能同时存在于多个限界上下文,如果在订单上下文和配送上下文都各自实现了运费计算的逻辑,就会使得这一重复蔓延到系统各处,一旦运费计算规则发生变化,就需要同时修改多个限界上下文,修改时,如果遗漏了某个重复的实现,还会引入潜在的缺陷。

3、业务能力违背了正交性,意味着业务服务出现的重复。例如,在一个物流系统中,地图上下文提供了地理位置定位的业务服务,结果在导航上下文又定义了这一服务。之所以出现这一结果,可能是因为各个领域特性团队沟通不畅。

b、单一抽象层次原则

识别出来的每个限界上下文在抽象层次上应该保持同一水平,不允许在抽象层次上出现彼此包含的情况

违背:

1、在一个集装箱多式联运系统中,商务上下文与合同上下文就不在一个抽象层次上,因为商务的概念实际涵盖了合同、客户、项目等更低抽象层次的概念

c、奥卡姆剃刀原理

即“若无必要勿增实体”,这里所谓的“实体”,指的是限界上下文,也就是说,是否要分离出一个独立的限界上下文,需要给出充分的理由

限界上下文的识别直接影响了领域驱动设计的架构质量。通过分解、归类、归纳到最后的验证之后,如果对识别出来的限界上下文的准确性依然心存疑虑,比较务实的做法是保证限界上下文具备一定的粗粒度

这正是奥卡姆剃刀原理的体现,即“切勿浪费较多东西去做用较少的东西同样可以做好的事情”,更文雅的说法就是“如无必要,勿增实体”。遵循该原则,意味着当我们没有寻找到必须切分限界上下文的必要证据时,就不要增加新的限界上下文。倘若觉得功能的边界不好把握分寸,可以考虑将这些模棱两可的功能放在同一个限界上下文中。待到该限界上下文变得越来越庞大,以至于一个领域特性团队无法完成交付目标;又或者违背了限界上下文的自治原则,或者质量属性要求它的边界需要再次切分时,再对该限界上下文进行分解,增加新的限界上下文。这才是设计的实证主义态度。

2)团队维度

1、根据团队人数,一般一个团队至多负责一个上下文

2、根据预估工作量,当一个限界上下文工作量过大,就要考虑拆分了

3、“任劳任怨”的好团队也不是真正的好团队,边界内的要积极,边界外的要“抱有成

3)技术维度

a、质量属性

领域驱动设计识别限界上下文,一定是领域维度优先,否则谈什么领域驱动设计呢?根据领域维度识别出限界上下文后,再考虑技术因素。如果确实因为某种技术原因,要求将一些业务服务独立出来,也就相当于给奥卡姆剃刀原则提供了分离的理由。

从技术维度看界限上下文,首先要关注目标系统的质量属性

如果认为某个界限上下文的部分业务功能不能满足质量属性要求,就需要调整界限上下文的边界,虽然变化因素是质量属性,但影响的内容确是对应的业务功能,为了不破坏设计的正交性,仍应按照业务变化的方向进行划分,也就是通过纵向划分,将质量因素影响的那部分业务功能,完整的分离出来。形成一个纵向的业务切面,组成一个单独的界限上下文,同时仍然在界限上下文内部保持菱形对此架构,以隔离业务功能与技术实现,并在一个更小的范围维持领域模型的统一性和一致性。

例如,因为性能或高并发的质量属性需求,要求某些业务服务必须使用专门的资源,由于限界上下文是业务能力的纵向切分,其边界内实际上还包括对数据的管理,要满足安全要求,也只能将其单独分离。

由此,可以从技术维度单独分离出限界上下文。这可以认为是技术维度对领域维度的一种干扰,或者说是质量属性对业务需求的影响。考虑到限界上下文属于解空间的范畴,适度考虑技术实现因素,也是合理的行为。

image-20240401191947218

image-20240401191933021

b、复用和变化

运用复用原则分离出来的界限上下文往往对应是支撑子领域,作为支撑功能又可以同时服务于多个界限上下文。

界限上下文对变化的应对,其实体现了单一职责原则,对于一个界限上下文不应该存在两个引起它变化的原因。如果出险率,则考虑将其中影响的上下文出来出来,让它独立演化

c、遗留系统

界限上下午自治原则唯一例外是遗留系统,如果目前系统需要和遗留系统协作,通过需要为它单独建立一个界限上下文。系统之所以要将现有遗留系统组单独作为一个界限上下文,要么是因为还要继续维护遗留系统,满足新增需要,要么一部分业务功能与遗留系统集成。

对于前者:遗留系统界限上下文定义了一个独立进化的自治边界,他能小心翼翼的控制新增需求的代码

对于后者:与之集成的界限上下文采用了菱形堆成架构,因此可以慢慢对遗留系统进行重构或者下线

ContactAuthor