领域:
广义上讲,领域(Domain)即是一个组织所做的事件以及其中所包含的一切。每个组织都有它自己的业务范围和做事方式。这个业务范围以及在其中所进行的活动便是领域。
领域既可表示整个业务系统,也可以表示其中的某个核心域或支撑子域。当谈及到业务系统中的某个方面时,使用”核心域“或”子域“以示区别。
为整个业务系统创建一个单一的、内聚的、全功能式的模型。这并不是我们使用DDD的目的。正好相反,DDD中,一个领域被分为若干子域,领域模型在限界上下文中完成开发。在开发一个领域模型时,我们关注的通常只是这个业务系统的某个方面。试图创建一个全功能的领域模型是非常困难的,对领域拆分有助于我们成功。
几乎所有软件的领域都包含多个子域,这和软件系统本身的复杂性没有太大关系。有时一个业务系统的成功取决于它所提供的多种功能,而将这些功能分开对待是有好处的。
子域:
子域并不是一定要做得很大,并且包含很多功能。有时,子域可以简单到只包含一套算法,但并不包含在核心域之中。简单的子域可以以模块的形式从核心域中分离出来,而不需要包含在笨重的子系统组件中。
很多软件开发者都认为将所有东西都放在一个系统里面是一件好事。其实不管你向系统中添加多少功能,你都无法满足每一个潜在客户的需求。如果不通过子域对软件模型进行划分,事情将变得更加烦琐,因为系统中的各个部分都是紧密联系在一起的。
限界上下文:
一般一个限界上下文对应一个子域。另外一个限界上下文有可能包含的不只一个子域。
如一个图书出版系统,图书生命周期的不同阶段(不同的上下文环境)
- 概念设计,计划出书
- 联系作者,签订合同
- 管理图书的编辑过程
- 将图书翻译成其他语言
- 出版纸质版或电子版图书
- 市场营销
- 将图书卖给销售商或直接卖给读者
- 将图书发送给销售商或读者
以上所有阶段,我们用一个单一的概念对图书建模显然不行,每个阶段“图书都有不同的定义”。一本书只有和作者签订了合同之后才能拥有书名,而书名可能在编辑过程进行修改。在编辑过程中,图书包含了一系列的稿件,其中包括注释和校正,走到最终定稿。图书印刷方使用页面布局和封面板式印制图书。营销员不需要编辑和印制,只需要图书的简介,图书售后物流,匀人需要图书的标识码、物流目的地、数目、尺寸和重量等。
如果我们用单一模型来处理所有这些阶段会发生什么?概念混淆、意见分歧和争论是不可避免的。即使有时可能会得到一个正确的公共模型,但这种模型并不具有持久性。
为解决这个问题,我们应该为每个阶段创建各自的限界上下文,在每个限界上下文 ,都存在某种类型的图书。在所有的上下文中,不同类型的图书对象将共享一个身份标识,这个标识可能是在概念设计阶段创建的。然而,不同上下文中的图书模型却是不同的。
如果在不同的限界上下文看到了完全相同的对象,这通常意味着你的模型是错误的,除非这些限界上下文使用了共享内核(Shared Kernel)。
限界上下文不仅仅只包含模型。虽然领域模型是限界上下文的主要“公民”。但是限界上下文并不只局限于容纳模型,它通常标定一个系统、一个应该程序或一种业务服务(表示一系统用于实现业务用例的复杂组件)。有时,限界上下文所包含的内容可能比较少,比如,一个通用子域便可以只包含领域模型。
模型应该用来驱动数据库和用户界面的设计,而不是反向驱动。
如下模型驱动数据库设计。
public class Backlogltem extends Entity {
private BacklogItemld backlogltemld;
private BusinessPriority businessPriority;
}
CREATE TABLE 'tbl_backlog_item' (
'backlog_item_id_id' varchar(36) NOT NULL,
'business_priority_ratings_benefit' int NOT NULL,
'business_priority_ratings_cost' int NOT NULL,
'business_priority_ratings_penalty' int NOT NULL,
'business_priority_ratings_risk' int NOT NULL,
)ENGINE=InnoDB;
通常情况下,系统/应用程序的使用者并不只是人,还可能是另外的计算机系统。系统中有可能存在Web服务之类的组件。我们也可以使用REST资源来与模型交互,此时的REST资源即被称为开放主机服务(Open Host Service),以上面向服务的组件都应该位于上下文边界之内。
用户界面和面向服务端点都会将操作委派给应用服务。应用服务包括比如安全和事务管理等。对于模型来说,应用服务扮演的是一种门面模式Facade。同时,应用服务还具有任务管理功能,它将来自用例流(Use Case Flow)的请求转换成领域逻辑的执行流。应用服务也是位于上下文边界之内。
限界上下文主要用来封装通用语言和领域对象,同时它也包含了那些为领域模型提供交互手段和辅助功能 的内容。
不要用架构来指导设计,而不是通用语言。一些平台、框架或者基础设施通常是用来打包和部署组件,它们可能影响我们对限界上下文的设计,此时我们会从技术层面而不是语言边界来考虑问题。
不要为了分配任务而拆分限界上下文。我们没有必要为了管理技术资源而创建一些假的上下文边界。
采用语言驱动来实施DDD,这里地底线:如果没有采用语言驱动,那么你就不算在和领域专家一起工作来创建限界上下文。认真考虑限界上下文的大小,不要急于将其小型化。
与技术组件保护一致。技术组件并不能定义限界上下文。一个限界上下文通常就一个工程项目。VS中在同一个解决方案中将用户界面、应用服务和领域对象分离在不同的子项止中是合理的。项目源代码可以只包含领域模型,也可以包含一些周边的层匮乏六边形区域。
在使用java时,顶层包名通常表示限界上下文中顶层模块的名字。