一文说透子域和限界上下文

2023-03-25  本文已影响0人  _张晓龙_

前言

子域(Subdomain)和限界上下文(Bounded Context,BC)是领域驱动设计(Domain-Driven Design,DDD) 中战略设计阶段的重要概念,但很多 DDD 学员却对这两个概念及其关系理解的不够透彻,难以有效应用在设计实践中。本文以代码打靶业务为例,尝试说透子域和限界上下文的概念及其关系。

假设我们通过事件风暴建立了代码打靶业务的统一语言,其中领域名词及其说明如下:

领域名词 说明
靶子 一段或几段存在缺陷的代码,可以有多个源码文件,一般200行左右,最多不超过500行
靶环 一条正确的缺陷记录
靶标 所有靶环的汇总,即靶子的所有靶环
语言 通用编程语言或 DSL,每类文档也可以对应一种自定义语言,一个靶子仅涉及一种语言
靶场 用户打靶时进入的场所,一个靶场可以选择多个靶子,一个靶子也可以被多个靶场选择
打靶记录 用户打靶时提交评审缺陷的最小单位,与靶环对应
答卷 用户将当前靶子打完后提交,形成一个答卷,包含一条或多条打靶记录
规范 缺陷分类标准
工作空间 隔离不同的规范,一个工作空间只能有一个规范
版本 一个规范可以有多个版本,可以同时生效,一个靶子对应一个规范的某个版本
靶子成绩 命中的靶环数和获得的总分
靶场成绩 靶场内多个靶子成绩的汇总
证书 提供 HTTPS 加密协议访问
用户 使用系统的人员
权限 对资源的访问、使用和操作能力控制的规则
角色 权限的集合
组织 企业经营活动过程中形成的一种管理结构
评价 对用户打靶能力的评估,比如用户画像,成长曲线
推荐 根据用户的能力弱项推送相关知识,这里将动词名词化了
个人信息 个人资料
我的靶场 我打过的靶场
我的靶子 我上传的靶子
个人看板 个人维度的度量标
组织看板 组织维度的度量指标
产品介绍 产品说明
帮助文档 用户手册
版本特性 每个版本的功能列表
关于我们 主创团队介绍

子域

领域建模首先是一个定义问题的方法,其次才是解决问题的方法。就是说,一方面,我们要知道解决的问题是什么;另一方面,我们要知道怎么去解决问题。

我们要解决的问题就是领域问题,在 DDD 中,通过子域模式先把问题从大面上进行分解。软件开发是解决问题,而解决问题要分而治之。所谓分而治之,就是要把问题分解了,对应到 DDD 中,就是要把一个大领域分解成若干个小领域,而这个分解出来的小领域就是子域。

现在我们的大领域是代码打靶领域,如下所示:


big-domain.png

分离子域

业务的目标是打靶。首先,我们把打靶相关的名词找出来,包括靶场、靶子、语言、靶标、靶环、答卷、打靶记录、靶子成绩和靶场成绩;其次,我们日常评审流程也可以通过打靶的方式来完成,这时领域名词并没有增加。我们将相关的领域名词分离出来,并命名为打靶子域。


hit.png

代码打靶业务的铁三角是规范、内容和工具,而在工具中要管理规范和内容的生命周期。接着,我们把规范管理和内容管理这两件事分离出来:

manager.png

对于用户,访问工具(系统)时都按照HTTPS协议访问,然后登陆。用户登录后,根据权限管理配置,大多数用户是打靶人员,而一小部分用户是管理员。对于打靶人员,因所属的组织不同会看到不同的靶场。这里涉及的领域名词包括用户、证书、权限、角色和组织。我们将相关的领域名词分离出来,并命名为安全子域。

每个用户都有个人信息,一般从配置服务统一获取,然后再让用户根据需要定制修改。打靶人员进入靶场完成打靶后,后续可以统一查看曾经打过的靶场。管理员上传靶子后,由于规范的升级,可能导致一些靶标涉及已废弃的规范条目,这时可以在自己已上传的靶子中统一核查这种情况,从而找到目标靶子,并对其靶标进行刷新。这里涉及的领域名词包括个人信息、我的靶场和我的靶子。我们将相关的领域名词分离出来,并命名为个人中心子域。

对于工具来说,需要向用户提供度量看板。这里涉及的领域名词包括个人看板和组织看板。我们将相关的领域名词分离出来,并命名为看板子域。

对于打靶人员,打靶不是目的,代码评审能力和编码能力提升才是。如何让打靶人员快速感知自己的提升?如何让打靶人员低成本知道自己的弱项?如何让打靶人员快速提升代码评审能力和编码能力?这是工具的重要扩展方向,涉及的领域名词包括评价和推荐。我们将相关的领域名词分离出来,并命名为智能学习子域。

最后,还剩下一组领域名词,包括产品介绍、帮助文档、版本特性和关于我们。我们将相关的领域名词分离出来,并命名为运营子域。

综上,我们看一下分离子域的全景图:


subdomain.png

区分子域

对于代码打靶领域而言,划分出来的子域有 8 个,但并非每个子域都一样重要。所以,我们还要把划分出来的子域再做一下区分,分成核心域(Core Domain)、支撑域(Supporting Subdomain)和通用域(Generic Subdomain)。

核心域是整个系统最重要的部分,是整个业务得以成功的关键。关于核心域,Eric 曾提出过几个问题,帮我们识别核心域:

如果你对这几个问题的回答能够帮你找到这个系统非写不可的理由,那它就是你的核心域。对于代码打靶领域来说,打靶子域、规范管理子域和内容管理子域都是核心域。

有一些子域不是你的核心竞争力,但却是系统不得不做的东西,市场上也找不到一个现成的方案,这种子域就是支撑域。对于代码打靶领域来说,智能学习子域、个人中心子域是和运营子域都是支撑域。

还有一种子域叫通用域,就是行业里通常都是这么做,即便不自己做,也并不影响你的业务运行。对于代码打靶领域来说,安全子域和看板子域都是通用域。

我们之所以要区分不同的子域,关键的原因就在于,我们可以决定不同的投资策略。核心域要全力投入,支撑域次之,通用域甚至可以花钱买服务。

对于代码打靶领域的 8 个子域,区分核心域、支撑域和通用域后,子域全景图变为:


image.png

限界上下文

通过分离子域,区分核心域、支撑域和通用域,我们将 DDD 在问题域的概念已经说清楚了。接下来,就要进入到解决方案域了。我们现在有了 8 个子域,如何将这些子域落实到代码上呢?首先要解决的就是这些子域如何组织的问题,是写一个程序将所有子域都放在里面,还是将每个子域分别放在一个独立的应用,亦或是有些放在一起,有些分开。这就引出了另一个重要的概念,限界上下文。

限界上下文,顾名思义,它形成了一个边界,一个限定了统一语言自由使用的边界,一旦出界,含义便无法保证。每个限界上下文有一个领域模型,保证了概念的严格一致性,而限界上下文之间则没有必要一致。这就是说,全局概念一致性从根本上是不可能的,我们不再追求全局一致性,而是退而求其次,仅追求局部的一致性,使概念不一致的问题得到合理管控,从而实现业务目标。

有了对限界上下文的理解,我们就可以将代码打靶业务拆分到不同的限界上下文中,但从哪里开始比较好呢?或许你也想到了,那就是子域。

我们先将每个子域对应一个候选限界上下文,然后再根据业务边界、工作边界和技术边界来拆分或合并不满足约束的候选限界上下文,形成真正的限界上下文

代码打靶领域有 8 个子域,分别是打靶子域、规范管理子域、内容管理子域、安全子域、个人中心子域、看板子域、智能学习子域和运营子域。根据每个子域对应一个候选限界上下文,那么我们就得到 8 个候选限界上下文,分别是打靶上下文、规范管理上下文、内容管理上下文、安全上下文、个人中心上下文、看板上下文、智能学习上下文和运营上下文。

我们简单介绍一下业务边界、工作边界和技术边界:

类型 说明
业务边界 对领域模型的控制,维护了模型的完整性与一致性,从而降低系统的业务复杂度
工作边界 对团队协作的控制,建立了团队之间的合作模式,避免团队之间的沟通变得混乱,从而降低系统的管理复杂度
技术边界 对技术风险的控制,做出对系统质量属性的响应与承诺,功能复用,管理变化,确定服务之间的集成方式,从而降低系统的技术复杂度

想深入了解业务边界、工作边界和技术边界的读者,可以阅读笔者的另一篇文章《聊聊服务化架构的边界》

经过对业务边界、工作边界和技术边界的分析,打靶候选上下文和安全候选上下文需要拆分,规范管理候选上下文和内容管理候选上下文需要合并,而其他候选上下文保持不变:

综上,我们的拆分限界上下文一共有 9 个:

对于每一个限界上下文,并不意味着一定要独立部署成一个微服务,要根据成本收益等因素来权衡。

两者关系

问题域和解决方案域有映射关系,那么子域和限界上下文的映射关系是什么?

代码打靶业务子域和限界上下文及其关系整理如下:

子域 限界上下文 关系
打靶子域 打靶上下文
日常评审上下文
一对多
规范管理子域
内容管理子域
靶场管理上下文 多对一
安全子域 反向代理上下文
访问控制上下文
一对多
智能学习子域 智能学习上下文 一对一
个人中心子域 个人中心上下文 一对一
看板子域 看板上下文 一对一
运营子域 运营上下文 一对一

可以看出,子域和限界上下文的关系有三种:

小结

本文结合代码打靶领域 DDD 战略设计实践案例,说透了子域和限界上下文的概念,并彻底理清了它们之间的关系:

上一篇 下一篇

猜你喜欢

热点阅读