领域:从广义上讲,领域是一个组织所做的事情以及其中所包含的一切。每个组织都有自己的业务范围和做事方式。这个业务范围以及在其中所进行的活动便是领域。领域既可以表示整个业务系统,也可以表示其中的某个核心域或支撑子域。
领域驱动设计的两个阶段分为战略设计和战术设计。
战略设计:
战略设计需要从问题空间和解空间两个维度来考虑。
问题空间:对问题空间进行合理分解,识别出核心子领域(即核心域)、通用子领域和支撑子领域,并确定各个子领域的目标、边界和建模策略。
解空间:对问题空间进行解决方案的架构映射,通过划分限界上下文,为统一语言提供知识语境,并在其边界内维护领域模型的统一。每个限界上下文的内部有着自己的架构,限界上下文之间的协作关系则通过上下文映射来体现和表达。
我们在战略层次从问题空间映射到解空间时,子领域也将映射到限界上下文。
战术设计:
战术设计阶段需要在限界上下文内部开展领域建模,前提是你为限界上下文选择了领域模型模式。战术设计阶段最重要的设计元模型是聚合模式。战略设计指导着战术设计,等同于设计原则指导着设计决策。
从战略设计到战术设计是自顶向下的设计过程,体现为设计原则对设计决策的指导;将战术设计方案反馈给战略设计,是自下而上的演化过程,体现为对领域概念的重构引起对战略架构的重构。
战略建模:
限界上下文
上下文映射图
战术建模:
聚合-Aggregate
实体-Entity
值对象-Value Objects
资源库-Repository
领域服务-Domain Services
领域实践-Domain Events
模块-Modules
核心域:整个业务领域的一部分,是促成业务领域的主要因素,一个产品要有竞争优势需要在核心域上胜人一筹
支撑子域:通常在开发中,我们会创建或购买某个限界上下文来支撑我们的业务,如果这样的限界上下文对应业务的某些重要但非核心方面,这便是一个支撑子域
通用子域:如果一个子域被用于业务整个系统,那么它便是一个通用子域
问题空间:是领域的一部分,在问题空间中,需要思考的是业务所面临的调整。对问题空间的开发将产生一个新的核心域。对问题空间的评估应该同时考虑已有子域和额外所需子域,因此问题空间是核心域和其他子域的组合
解决空间:是领域的一部分,在解决空间中,我们思考的是如何通过软件实现来解决业务挑战(问题空间)。解决空间包括一个或多个限界上下文(即一组特定的软件模型)。因为限界上下文即是一个特定的解决方案,通过软件的方式来实现解决方案。
合作关系:两个限界上下午的团队要么一起成功,要么一起失败,他们应该为此建立一种合作关系。
客户方-供应方开发:处于上下游关系的两个团队。
共享内核:模型和代码的共享会产生一种紧密的依赖性,应该为共享的部分指定一个显示的边界,且保持共享内核的小型化,在没有和其他团队沟通的情况下,共享内核是不允许变动的。
尊奉者:存在上下游关系的两个团队,出于某些原因,上游团队无法为下游团队提供所需的帮助但又作出了承诺,这些承诺往往无法实现,此时,下游团队只能盲目的使用上游团队的模型。
发布语言:两个限界上下文之间翻译模型需要的公用语言。
防腐层:在共享内核、合作关系或客户方、供应方关系难以实现时,下游团队根据自己的领域模型创建的一个单独的代理层,上游通过该代理层向下游系统提供功能。
开放主机服务:定义的一种协议,子系统通过该协议访问你的服务。应该公共该协议,便于任何想与我们集成的人都能使用该协议。
大泥球:系统中会存在混杂在一起的模型,模型之间的边界很模糊。
领域驱动设计过程:
贯穿了整个软件构建的生命周期,包括对业务需求的探索和分析,系统的架构和设计,编码实现,测试和重构;
1.面对客户的需求,由领域专家与开发团队展开充分交流,经过需求分析与知识提炼,获取清晰明确的问题空间,并从问题空间的业务需求中提炼出统一语言(通用语言);
2.利用子领域分解问题空间,根据价值高低确定核心子领域(即核心域)、通用子领域和支撑子领域;
3.通过对问题空间开展战略层次的求解,获得限界上下文形成解空间的主要支撑元素。识别限界上下文的基础来自问题空间的业务需求,遵循高内聚低耦合的原则划分领域知识的边界,在通过限界上下文映射管理它们之间的关系;
4.进入战术设计阶段,进行面向领域的模型驱动设计;
5.选定一个限界上下文,在统一语言的指导下,针对该限界上下文内部的领域知识开展领域模型驱动设计。首先进行领域分析,提炼领域知识建立满足统一语言要求的领域分析模型,然后引入实体、值对象、领域服务、领域事件、聚合、资源库与和工厂等设计要素开始程序设计,获得设计模型后在它的指导下进行编码实现,输出最终的领域模型。
一个提供云端侧执行能力的领域模型如图1.1所示(仅供参考,能力有限,不一定合理)
图1.1 领域模型示例
补充说明,公用告警是由第三方提供,那么对提供方来说,告警就为核心域(是他们的核心竞争力),我们可以选择不同三方提供的告警能力为我们的业务服务。
限界上下文是一个显示的边界,领域模型存在于这个边界之类。领域模型把通用语言表达成软件模型。创建边界的原因在于每一个模型(属性&操作)在边界之内具有特定的含义。
考虑一种情况,如果在不同模型中存在相同或相似的对象,但他们在各自模型中的意思不同。如果没有边界,那么我们将无法区分他们具体的含义。当模型被一个显示的边界所包围时,其中每个概念的含义便确定了。即限界上下文主要是一个语言上的边界。以账户模型为例,来简单说明下这种语言上的边界。如图2.1所示:
图2.1 账户的不同含义
在音乐上下文中,account表示一个用户下载音乐,喜欢音乐,订阅音乐等的情况。而在银行上下文中,account表示一个用户在银行的存款,交易等信息。
在实施DDD中,通常情况下,我们所面对的都是一些区别较小的概念,因为在一个上下文中,我们通常根据通用语言来命名某个概念,没必要刻意的命名一个概念保持与上下文一致。比如,我们可以在音乐播放器上下文中定义账号为音乐账号,在银行上下文中定义账号为银行账号。这两个概念我们都可以用账号表示,因为限界上下文已经帮我们做了区分。(注:这里假定音乐播放器上下文和银行上下文属于同一个领域)
思考:如何确定在自己的领域中,限界上下文的划分是否恰当呢?如果能够在不同的限界上下文中识别出区别微小的概念,那么这个限界上下文的划分是合理的;如果在不同的限界上下文中,看到了完全相同的对象,一般来说,这样的划分或模型是错误的。除非这些限界上下文使用了共享内核
上下文映射图能够表现项目的当前状态,通过当前状态能够了解我们所处的位置,并帮助决定下一步如何做。 上下文映射图展现了一种组织动态能力,可以帮助我们识别出阻碍项目进展的一些管理问题。在上下文映射图中,通常ACL表示防腐层,OHS表示开放主机服务,PL表示发布语言。
下面以图1.1的领域模型为例,阐述下是如何通过上下文映射图逐步构成上诉的领域模型的。从业务定位出发,我们是要构造一个最优测试能力的产品,自然而言的这个最优测试能力成为了我们的核心域,便有了最优测试能力上下文。进一步,测试是基于任务维度执行的,因此需要有一个任务管模块,在将任务管理模块从最优测试能力这个核心域剥离出来的时候,存在如图3.1的上下文映射图。
图3.1 上下文映射图1
从图3.1能看出,在这个过程中,我们确定的知道需要一个最优测试能力上下文,同时希望创建一个任务管理上下文,但是在创建任务管理上下文的时候,出现一个问题,就是如何将任务管理从最优测试领域中剥离出来。上图中的感叹号表示一个狭窄通道,这个狭窄的通道往往容易有一些外部概念混淆进来。从DDD的角度出发,我们希望最优测试能力这个核心域的上下文能够控制所有的进出。任何进入边界的外部概念都需要有充分的理由 。
通过进一步分析,我们意识到任务管理并不属于最优测试能力上下文,我们需要将任务管理剥离出来,只有在经过最优测试能力的同意下,才能进入核心域,因此产生了两个领域:最优测试能力核心域和任务管理支撑子域,如图3.2所示。
图3.2 上下文映射图2
接下来,我们需要对上诉的上下文映射图进行进一步处理来明确两个上下文之间的沟通方式。进一步,将任务管理支撑域从最优测试能力核心域中安全剥离出去,如图3.3所示:
图3.3 上下文映射图3
图3.3中,D表示下游服务,U表示上游服务, OHS表示开放主机服务,PL表示发布语言,ACL表示防腐层。
其中,OHS可以通过REST实现,可以将OHS看出RPC;也可看作通过消息机制来实现;PL可以通过多种方式实现,如XML,JSON等。如在REST中,PL用了表示领域概念,可以是XML或JSON。在下游上下文中,可以为每个ACL定义相应的领域服务。同时,也可以将ACL用于资源库接口。在使用REST时,客户端的领域服务访问远程的OHS,远程服务以PL的形式返回,下游的ACL将返回的内容翻译成本地上下文的领域对象(比如,java中的某些POJO,DTO这种)。
进一步,将图3.3中ACL和OHS展开,如图3.4所示。
图3.4
最优测试能力系统(下游系统)中的边界对象通过同步的方式向任务管理系统(上游系统)获取任务资源。获取到任务资源后,边界对象取出数据,再将数据翻译成适当的值对象。
持续更新中。。。。。。。。。。
参考
1.一个限界上下文并不只包含领域模型,它通常标定了一个系统,一个程序或者一种业务服务
2.为了分配任务而拆分限界上下文是一种错误的上下文建模方式
3.创建限界上下文只是为了架构组件或开发者资源这样的考虑,会导致通用语言变得四分五裂
4.使用隔离内核的时机是当有一个非常重要的限界上下文,但模型中的关键部分却被大量起辅助作用的功能掩盖