领域、子域和限界上下文概述

1、概述

1.1 领域

广义领域:领域(Domain)即是一个组织所做的事情以及其中所包含的一切。每个组织都有它自己的业务范围和做事方式。这个业务范围以及在其中所进行的活动都有它自己的业务范围和做事方式。这个业务范围以及在其中所进行的活动便是领域。当你组织开发软件时,你所面对的便是这个组织的领域。

领域既可以表示整合业务系统,也可以表示其中的某个核心域或者支撑域。在DDD中,一个领域被分成若干子域,领域模型在限界上下文中完成开发,在开发一个领域模型时,我们关注的通常只是这个业务系统的某个方面。

1.2 子域

子域是领域更细粒度的划分,根据重要性与功能将领域分为大致三类的多个子域,分别是核心子域、支撑子域和通用子域。核心域是业务成功的主要促成因素,主要竞争力,支撑子域是支撑核心域的,而通用子域是业务系统的公用部分。支撑子域在领域中是可以有多个的,核心域应该是只有一个为好,当然,除了核心域外,其他子域也可以没有。

核心域是最重要的,开发核心域的解决方案是一种关键性的业务的投入,应该给予核心域的开发最高的优先级和最优秀的开发团队。如果核心域中的一些功能可以从核心域中分离出来,那我们可以用模块的形式从核心域分离出来,这里说的模块其实就是java中说的包,也可以理解为命名空间,文件夹。

1.3 限界上下文

限界上下文包含的是一个系统、一个应用、一种业务服务以及一系列实现业务的复杂组件。

比如说在一个电子商务服务系统的领域中,加入一个库存系统和一个外部的预测系统,那么这个领域就可以拥有对应的三个限界上下文,一个限界上下文可能被包含在一个子域里,也可能多个子域,比如电子商务系统就包含了发票子域、订单子域等。

2、工作中的子域和限界上下文

对于如何使用子域,先看下一个简单的例子:一个零售商在线销售产品的例子。

要在这个领域中开展业务,该零售商要展示不同类别的产品,支持下单和付款,还需要安排物流。在这个领域中,零售商的领域可以分为4个主要的子域:产品目录(Product Catalog)、订单(Order)、发票(Invoicing)和物流(Shiping),下图是一个含有子域和限界上下文的领域,它的上半部分表示了这个的一个电子商务系统。

在这里插入图片描述
这看起来比较简单,一个领域包含四个子域。但是考虑加入一些额外细节,以上这个例子将变得复杂,如果向上述系统加入一个库存(Inventory)系统和外部接入的预测库存系统,如上图下半部分。

目前,该零售商的领域中只包含了三个物理系统,其中两个是内部系统。这两个系统表示两个限界上下文。这导致了几个子系统承担了太多的业务功能。

在上面的电子商务系统限界上下文中,可以找出多个隐式的领域模型,这些领域模型被融合成了一个软件模型。随着各个逻辑模型中不断加入新的功能,他们之间的复杂关系对于每个模型都是一个阻碍,这些问题导致的原因通常都是由于软件的关注点没有得到清晰的划分导致。

通过使用DDD战略设计工具,我们可以按照实际功能将这些交织的模型划分为逻辑上相互分离的子域,从而在一定程度上减少系统的复杂性。逻辑子域的边界如上图中的虚线所示。在不同的逻辑子域之间或者不同的物理限界上下文之间均有划线,这表示它们之间存在集成关系。

库存问题也是电商系统中的一个重要问题,库存各种货物存多少合适呢?一种好的做法是,根据过去的销售趋势来制定未来的库存计划,从而达到优化库存系统的目的。

增加一个预测引擎意味着开发一个新的核心域。如果这个新的解决方案是一个核心域,那么开发团队将从周围的逻辑子域以及集团体系中收益。因此,在该核心域的项目启动时,上图已有的集成体系对于掌握项目情况来说起到关键作用。

子域并不一定做得很大,并且包含很多功能。有时,子域可以简单到只包含一套算法,这套算法可能对于业务系统来说非常重要,但是并不包含在核心域之中。在正确实施DDD的情况下,这种简单的子域可以以模块(module)的形式从核心域中分离出来,而并不需要包含在笨重的子系统组件中。

在实施DDD的时候,致力于将限界上下文中领域模型所用到的每一个术语都进行上下文限界区分。这种限界主要是语言层面上的上下文边界,也是实现DDD的关键。

在上图中,哪种类型的限界上下文在语言层面上设计的更好呢?
在电子商务系统中,当我们谈到四个子域时,可以非常确定的有些术语是存在冲突的。如”顾客“,在浏览产品目录时,”顾客“被放在了先前购买的情况、忠诚度、可买产品、折扣和物流方式这样的上下文中。而在下单时,”顾客“的上下文包括名字、产品寄送地址、订单总价和一些付款术语。在这个电子系统中,”顾客“并没有一个清晰的定义。在一个好的上下文中,每一个术语仅表示一种领域概念。

然而,我们也不能保证库存系统的模型就是完全清晰的,并且使用了完全非歧义的领域语言。比如”库存件“概念,已经被订购但还无法销售的产品称为延期订单件;保存在仓库中的产品称为积压件。等等。

从表面上来看,可以得出结论,库存系统比电子商务系统具有更高的DDD健康指数,原因是库存系统的开发团队并没有使用一个库存件来表示所有的概念。虽然也不确定这一定吗,但是可以肯定:库存模型比电子商务系统模型更容易集成。

一个企业的限界上下文并不是孤立存在的。不同子域之间的实线表示集成关系,这样表明不同的模型时需要协同工作的。集成的方式有很多种,将在上下文映射图中学到不同的集成方案。

3、将关注点放在核心域上

下图是一个领域的抽象视图,它可以表示任何一个领域,但是仅仅表示在某个时刻,从某个角度看的业务模型。

在这里插入图片描述

核心域:它是整个业务领域的一部分,也是业务成功的主要促成因素。从战略层面上讲,企业应该在核心域上胜人一筹。在实施DDD的过程中,应该主要关注核心域。

**支撑子域:**有时,我们会创建或者购买某个限界上下文来支撑我们的业务。如果这样的上下文对应着业务的某些重要方面,但却是不核心,那么它便是一个支撑子域。创建支撑子域的原因在于它们专注于业务的某个方面。

**通用子域:**如果一个系统被用于整个业务系统,那么这个子域便是通用子域。

并不能说支撑子域和通用子域是不重要的,它们是重要的,只是我们对他们的要求并没有像核心域那么高。

4、现实世界中领域和子域

领域中还同时存在问题空间(problem space)和解决方案空间(solution space)。在问题空间中,我们思考的是业务所面临的挑战,而在解决问题空间中,我们思考的是如何实现软件以解决这些业务挑战。

  • 问题空间是领域的一部分,对问题空间的开发将会产生一个新的核心域。对问题空间的评估应该同时考虑已有子域和额外所需子域。因此,问题空间是核心域和其他子域的组合。问题空间中的子域通常随着项目的不同而不同,他们各自关注于当前的业务问题,这使得子域对于问题空间的评估非常有用。子域允许我们快速地浏览领域中的各个方面,这些对于解决特定的问题是必要的。

  • 解决方案空间包括一个或多个限界上下文,即一组特定的软件模型。这是限界上下文中即是一个特定的解决方案,它通过软件的方式来实现解决方案。

为了澄清问题空间和解决方案空间的区别,来看个例子。

例如大型的ERP系统,我们可以将一个ERP系统想象成一个单一的限界上下文。由于ERP系统提供许多模块化的业务服务,将不同的模块看成不同的领域是有好处的,比如,可将库存模块和采购模块拆分成不同的逻辑子域,分别命名为库存子域和采购子域。

在这里插入图片描述

上图表示的是一个ERP系统的领域,也是上述抽象领域模板的一个实例。该企业计划开发一个特定的领域模型来降低成本,该模型可以为采购人员提供决策工具,这个新的核心域可以提高该企业的竞争优势。

在实施某个解决方案之前,我们需要对问题空间和解决方案空间进行评估。为了项目朝着正确的方向进行,需要先回答以下问题:

  • 这个战略核心域的名字是什么,它的目标是什么?
  • 这个战略核心域中包含哪些概念?
  • 这个核心域的支撑子域和通用子域是什么?
  • 如何安排项目人员?
  • 你能组建一支合适的团队吗?

在理解问题空间之后,来看看解决方案空间。对于问题空间的评估也是有益于理解解决方案空间的。解决方案空间在很大程度上受到现有系统和技术的影响。这里,我们应该根据分离的限界上下文仔细地思考,考虑以下问题:

  • 有哪些软件资产是已经存在的,它们可以重用吗?
  • 哪些资产是需要创建的,或者从别处获得?
  • 这些资产是如何集成在一起的?
  • 还需要什么样的集成?
  • 假如已经有了现有资产和那些需要被创建的资产,我们还需要做什么?
  • 核心域和那些支撑项目的成功率如何?会不会由于其中一个失败而导致整个项目失败的可能性?
  • 在哪些地方我们使用了完全不同的术语?
  • 限界上下文之间在哪些地方存在概念重叠?
  • 这些重叠的概念在不同的限界上下文之间是如何映射和翻译的?
  • 哪些限界上下文包含了核心域的概念,其中使用了哪些战术模式?

在上图中,用于捕获决策工具和算法的采购模型表示了核心域的解决方案。该领域模型将在最优获取上下文中实现,该上下文与最优获取核心域存在着一对一的关系。由于只对应一个子域,同时又拥有优秀的领域模型,这个最优获取上下文将会是业务领域中最好的限界上下文之一。

在上下文映射图中,将主要介绍通过集成上下文的方式来完成不同通用语言之间的映射。

5、理解限界上下文

限界上下文是一个显式的边界,领域模型便存在这个边界之内。领域模型把通用语言表达成软件模型。创建边界的原因在于,每一个模型概念,包括它的属性和操作,在边界之内都具有特殊的含义,且每个概念的含义便是确定的。因此,限界上下文主要是一个语义上的边界,我们应该通过这一点来衡量对一个限界上下文的使用正确与否。

限界上下文并不旨在创建单一的项目资产,让我们来看下一个账户(Account)模型在银行上下文(Blanking Context)和文学上下文(Literary Context)中的不同,如下表:

在这里插入图片描述

在图中,光凭名字我们根本无法区分两个账户的意思。只有通过它们所在的限界上下文我们才能看出他们之间的区别。

在这里插入图片描述

这两个限界上下文可能不属于同一个领域,这里说明:上下文才是王道。

  • 限界上下文不仅仅只包含模型

一个限界上下文并不是只包含领域模型,诚然,模型是限界上下文的主要“公民”。但是,限界上下文并不只局限于容纳模型,它通常标定了一个系统、一个应用程序或者一种业务服务。有时,限界上下文所包含的内容可能比较少,比如,一个通用子域便可以只包含领域模型。

当模型驱动着数据库Schema的设计时,此时的数据库scheme也应该位于该模所处的上下文边界之内。

限界上下文主要用来封装通用语言和领域对象,但同时它也包含了那些为领域模型提供交互手段和辅助功能的内容。需要注意的是,对于架构中的每个组件,我们都应该将其放在适当的地方。

  • 限界上下文的大小
    限界上下文可以包含多少领域模型中的基础部件呢,比如,模块、聚合、领域事件和领域服务等。限界上下文应该足够大,以能够表达它所对应的整套通用语言。但不要将核心域之外的概念不应该包含在限界上下文中。如果有一个概念不属于你的通用语言,那么一开始你就不应该将其引入到模型中。

  • 与技术组件保持一致
    将限界上下文想成是技术组件并无大碍,只是需要注意:技术组件并不能定义限界上下文。来看一些构成和部署限界上下文的常见做法。

当使用IDEA时,一个限界上下文通常就是一个工程项目。在使用Java时,顶层包名通常表示限界上下文中顶层模块的名称。对于上文中提到的例子,我们可以采用以下方式来定义包名:

com.chunsoft.optimalpurchasing

限界上下文的源代码结构可以根据职责做进一步分解。下面是一些二级包名:

com.chunsoft.optimalpurchasing.presentation
com.chunsoft.optimalpurchasing.application
com.chunsoft.optimalpurchasing.domain.model

请注意,即便存在这种模块化的拆分,团队依然应该只工作在一个限界上下文中。

©️2020 CSDN 皮肤主题: 黑客帝国 设计师:上身试试 返回首页