发布语言是一种公共语言,用于两个限界上下文之间的模型转换。防腐层和开放主机服务都是访问领域模型时建立的一层包装,前者针对发起调用的下游(通过基础设施层体现),后者针对响应请求的上游(通过应用层 远程服务),以避免上下游之间的通信集成将各自的领域模型引入进来,造成彼此之间的强耦合。因此,防腐层和开放主机服务操作的对象都不应该是各自的领域模型,这正是引入发布语言的原因。(对于熟悉云API的小伙伴就会发现,其实云API根据我们定义的接口生成对应的Request对象和Response对象,并集成在云API的SDK中,这些对象就是发布语言)。
一般情况下,发布语言根据开放主机服务的服务契约进行定义。
说到这里,我们惊讶地发现防腐层,开放主机服务和发布语言可以完美联动!
6.3.4 共享内核共享内核指将限界上下文中的领域模型直接暴露给其他限界上下文使用。注意,这会削弱了限界上下文边界的控制力。上面我们讲述的防腐层、开放主机服务以及发布语言无不传达一种思想,限界上下文不能直接暴露自己的领域模型或直接访问其他限界上下文的领域模型,一定要有隔离层!
但是,在特定的场景下,共享内核不见得不是一种合理的方式。任何软件设计决策都要考量成本与收益,只有收益高于成本,决策才是合理的。一般对于一些领域通用的值对象是相对稳定的,这些类型通常属于通用子领域,会被系统中几乎所有的限界上下文复用,那么这些领域模型就适合使用共享内核的方式。共享内核的收益不言而喻,而面临的风险则是共享的领域模型可能产生的变化。
6.3.5 合作者合作关系指的是协作的限界上下文由不同的团队负责,且这些团队之间具有要么一起成功,要么一起失败的强耦合关系。合作者模式要求参与的团队一起做计划、一起提交代码、一起开发和部署,采用持续集成的方式保证两个限界上下文的集成度与一致性,避免因为其中一个团队的修改影响集成点的失败。
6.3.6 客户方/供应方当一个限界上下文单向地为另一个限界上下文提供服务时,它们对应的团队就形成了客户方/供应方模式。这是最为常见的团队协作模式,客户方作为下游团队,供应方作为上游团队,二者协作的主要内容包括:
- 下游团队对上游团队提出的服务
- 上游团队提供的服务采用什么样的协议与调用方式
- 下游团队针对上游服务的测试策略
- 上游团队给下游团队承诺的交付日期
- 当上游服务的协议或调用方式发生变更时,如何控制变更
分离方式的团队协作模式是指两个限界上下文之间没有一丁点关系。如果此时双方使用到了相似/相同的领域模型,则可以通过拷贝的方式解决,保证限界上下文之间的物理隔离!
6.3.8 遵奉者当上游的限界上下文处于强势地位,且上游团队响应不积极时,我们可以采用遵奉者模式。即下游严格遵从上游团队的模型,以消除复杂的转换逻辑。
当下游团队选择“遵奉”于上游团队设计的模型时,意味着:
- 可以直接复用上游上下文的模型(好的);
- 减少了两个限界上下文之间模型的转换成本(好的);
- 使得下游限界上下文对上游产生了模型上的强依赖(坏的)。
一定要避免制造大泥球!大泥球的特点:
- 越来越多的聚合因为不合理的关联和依赖导致交叉污染;
- 对大泥球的维护牵一发而动全身;
- 强调“个人英雄主义”,只有个别“超人”能够理清逻辑。
示例-SMS的限界上下文可划分为:
- 成绩上下文
- 课程上下文
- 审批上下文
- 权限上下文
- 邮件上下文
上下文映射图如下所示。
7.领域建模阶段领域建模阶段由领域分析建模,领域设计建模和领域实现建模组成。在正式讲解建模活动前,先了解一下什么是模型驱动设计。
7.1 模型驱动设计模型是一种知识形式,它对知识进行了选择性的简化和有意的结构化,从而解决信息超载的问题。模型便于人们理解信息的意义,并专注核心问题。
建模过程一般由分析活动、设计活动和实现活动组成。每一次建模活动都是一次对知识的提炼和转换,并产生相应的模型,即分析模型、设计模型和实现模型。
建模过程并非是分析、设计和实现单向的前后串行过程,而是相互影响,不断切换和递进的关系。模型驱动设计的建模过程是:分析中蕴含了设计,设计中夹带了实现,甚至实现后还要回溯到设计和分析的一种迭代的、螺旋上升的演进过程。
根据分解问题的视角不同,我们日常建立的模型可以大致分为以下三类:
- 数据模型:将问题空间抽取出来的概念视为数据信息,在求解过程中关注数据实体的样式和它们之间的关系,由此建立的模型就是数据模型。
- 服务模型:将每个问题视为目标系统为客户端提供的服务,在求解过程就会关注客户端发起的请求以及服务返回的响应,由此建立的模型就是服务模型。
- 领域模型:围绕问题空间的业务需求,在求解过程中力求提炼出表达领域知识的逻辑概念,由此建立的模型就是领域模型。
一个优秀的领域模型应该具备以下的特征(我们也可以说具备这些特征的模型就是领域模型):
- 运用统一语言来表达领域中的概念;
- 蕴含业务活动和规则等领域知识;
- 对领域知识进行适度的提炼和抽象;
- 由一个迭代的演进过程建立;
- 有助于产品、领域专家和开发同学进行交流。
领域建模阶段目的便是建立领域模型。领域模型由领域分析模型、领域设计模型以及领域实现模型共同组成,它们也分别是领域分析建模、领域设计建模和领域实现建模三个建模活动的产物。
值得注意的是,领域模型并非由开发团队单方面输出的产物,而是由产品、领域专家和开发团队共同协作的结果。领域专家通过领域模型能够判断系统所支持的领域能力,以及由此编排出来的上层业务能力;开发团队通过领域模型能够形成基本的代码框架(包括架构分层,每层需要定义的接口,接口的命名等)。同理,领域模型的调整,也意味着领域知识或业务规则的变化,也预示着系统所支持的业务能力和代码实现同样需要作出改变。
7.2 领域分析建模领域分析建模:在限界上下文内,以“领域”为中心,提炼业务服务中的领域概念,确定领域概念之间的关系,最终形成领域分析模型。领域分析模型描述了各个限界上下文中的领域概念,以及领域概念之间的关系。
下面讲述如何通过“快速建模法”来构建领域分析模型。
7.2.1 名词建模找到业务服务中的名词,在统一语言指导下将其映射为领域概念。
7.2.2 动词建模识别动词并不是为领域模型对象分配职责、定义方法,而是将识别出来的动词当做一个领域行为,然后看它是否产生了影响管理、法律或财务的过程数据。若存在,则将这些过程数据作为领域概念放到领域分析模型中。注意,这里的过程数据是要求会对企业运营和管理产生影响的数据,比如示例-SMS系统中老师提交修改申请,就会产生申请单这个过程数据,而请求流水记录、任务执行记录都不属于过程数据。动词建模通过分析领域行为是否产生过程数据来找到隐藏的领域概念,弥补了名词建模的不足。
特别地,对于会产生领域事件的动词,一般可以抽象出一个已完成该动作的状态。
7.2.3 提取隐式概念除了“名词”和“动词”,概念中其他重要的类别也可以在模型中显式地表现出来,主要包括:约束和规格。
约束
约束一般是对领域概念的限制,我们可以将约束条件提取到自己的方法中,并通过方法名显式地表达约束的含义。比如示例-SMS中关于GPA运算的约束。