在我的博客阅读本文
领域驱动设计共有两个部分:
战略设计也可理解为策略设计,是从宏观角度着眼于领域的分析设计,属于系统分析阶段,注重如何从有界上下文中寻找领域模型,战略模式由有界上下文、无所不在的语言和上下文映射组成
战术设计属于设计代码阶段,使用聚合、实体、值对象等对象类型概念表达领域模型。
DDD有界上下文属于DDD战略设计的重要概念。
有界上下文:领域即边界,边界靠分类,分类需从内外部入手。
有界上下文是指在空间或时间上有边界的一段环境背景,它确定了每个模型的适用范围,模型体现了这个范围内的逻辑一致
有界上下文是根据逻辑一致性划分软件的模式。也就是说:有界上下文是人根据客观事物中的一致性逻辑去划分软件。注意上下文不是软件中的模块,而是一个微服务。
统一语言是大家统一使用的术语,不会让别人产生歧义。
统一语言是在整个团队的协商下发展起来的,这种无所不在的、通用语言必须在团队成员之间的任何场合都可以表达,以及可以用软件模型来表示。它是由团队中的开发人员、领域专家和其他参与者共享的语言。统一语言不是由领域专家指定的语言,而是应该由每个参与者共同协商,所以,类似ERP里面指定术语的方式是不可行的,因为即使人们被强行接受这些标准用语,对其理解也可能是片面的。它不应该在组织中由上而下,而应该由下而上整理出来。
没有统一语言的后果:
统一语言必须在领域模型中表达出来,主要体现在领域模型中的名称上。
方法论:
最终流程:
这里贴一段原文作为补充:
通过业务规则有助于发现强烈的逻辑关系,这是逻辑一致性的体现,进而可以发现最小边界的有界上下文,然后通过上下文中的模型显式表达这种逻辑一致性,这样就能完成DDD建模中最具创新的环节。当然,对于逻辑一致性的认识不是一蹴而就的,业务规则、业务流程只是帮助发现它的手段之一,找到事物内在的一致性需要具有深入的领域知识以及进行不断的思想碰撞。
有界上下文之间关系的处理基本原则是以松耦合、解耦合为主,因为不同的有界上下文有不同的团队、代码库、技术体系,如果两两之间过于耦合,就会发生两个团队经常在一起开会沟通、影响效率的情况,也可能是两个上下文的边界划分得不清晰,需要重新审视。
常见的关系:
共享内核:比如两个有界上下文共同使用一份代码内核
**开放主机服务也是一种上下文关系的映射,也称为上下游关系映射或API调用,一个上下文通过RPC等同步方式调用另外一个上下文的API,**调用者是被调用者的客户端。
这种方式在如今的微服务架构中比较普及。
这种模式在新旧上下文系统之间使用时,需要引入防腐层,防止两个上下文系统直接耦合,旧的上下文系统会影响新上下文系统的代码编写思路,因此必须引入第三层解耦。
发布订阅模式
发布的语言(PublishedLanguage):两个有界上下文中的模型需要一种共同语言进行相互翻译转换
为了实现公司最终的大目标,必须在不同的小目标范围中工作,它们被称为子域,因为它们自身并不足以使公司取得成功,合起来以后共同构成了公司的业务领域。
子域分为核心子域、支持子域和通用子域。
团队管理最佳实践:
**一个有界上下文对应一个子域,对应一个团队,对应一个微服务,**当然,也不能僵化执行这种对应关系,还是需要根据问题空间的子域划分、解决方案空间的上下文划分,以及团队人员能力等因素综合分析。
首先是按照时间顺序对每个功能排序,然后再进入功能内部考察其职责。这个功能的目标是什么?主要关心的是什么?这些都有助于**发现业务规则和不变性约束,将逻辑一致的规则约束合并为同类项,也就是合并成一个有界上下文,这是一种通过合并方式划分有界上下文的方式。**这种方式的核心还是根据业务规则来判断有界上下文,业务规则本身只是逻辑一致性的另外一种表达而已。
DDD建模过程中需要一种语言来描述业务领域,可以使用UML语言(一套面向对象分析设计的统一语言规范),DDD中一般需要用到以下3种:
从整个时序图中鸟瞰所有功能发生的时间顺序,从而能不断迭代调整上下文的边界。这是一个迭代、动态的变化过程,有界上下文不是一次设计完成,设计好了就放到另外一张图中,还要将不同上下文纳入整个领域背景下串联起来,看看是否能完整覆盖整个领域边界。这是一种领域流故事法(Domain Flow Storytelling),有界上下文本身就是故事中的演员。因此,故事始于用户实现的功能目标,然后是有界上下文之间的交互,检查这样能否最终提供整个解决方案。
**领域故事(Domain Storytelling)**是由Stefan Hofer在https://domainstorytelling.org提出的一种非常轻量级的工作坊形式,通过讲述故事的方式,专注于领域知识,通过领域语言来探索和理解用户流程、工作程序和整个业务流程与规则。
领域故事的特点是能够按照字面意思,逐字逐句地“观察”和“改进”领域语言。
这个是比较抽象的,这里整理了下书中的一个例子。假设分析航空货运运输的领域系统,技术人员会与相关管理人员或领域专家交谈:
最后一句回答中的“客户需要托运一批货物”,主语是“客户”,谓语动词是“托运”,宾语是“货物”,可以使用不同颜色表示它们:黄色表示主语,粉红色表示谓语,蓝色表示宾语,具体颜色取决于自己。
最后一句回答中的三句话其实代表了航空公司的内部流程。
参会者使用这种讲故事的对话方式,然后将对话结果使用主谓宾的颜色便签贴到墙上,随着时间推移,整个墙面可能被铺满。这时会看到整个领域的一个大流程图,用这种方式应对复杂枯燥的业务领域相当有效。
在这之后,可以使用流程标准语言BPMN来准确描述业务流程。
领域故事和业务流程常常是相关的,关键是需要从中找到不容易变化的业务能力,根据业务能力划分有界上下文的边界。
比如「根据组织架构划分有界上下文」与「根据能力划分有界上下文」比起来,尽管前者更容易,但是组织架构是容易变化的,因此根据后者来划分更合理。但是,没有组织机构作为主语,也难以发现谓语动词的能力。(作者原文说这是传统哲学和海德格尔哲学的区别)
DDD专家NickTune提倡使用填写表格的方式来逐步划分上下文边界,其表格内容如下:
这样的表格填写不是一蹴而就的,需要反复迭代,通过讲故事的形式反复和领域专家确认,注意其主谓宾用法,捕捉其语义中的业务策略、业务规则和能力,通过反复召开的头脑风暴会议最终确认。头脑风暴主要着重于寻找领域中的活动或事件,它们的一致点都是按时间线发现流程中的动词:动作行为、活动或事件。
时间线容易与流程混淆,并不是所有业务流程都是按照时间顺序编排的,有的流程可以并行发生,流程也可以组合,在事件风暴法中,通过发现发生的事件或活动来划分上下文边界,这种方法与流程更相关些,因为事件活动是流程的重要组成部分,而通过时间线发现上下文谈论的是从纯时间线来探索上下文边界。
事件风暴是围绕领域事件展开的头脑风暴会议,领域事件是什么,这是领域专家对其业务领域感兴趣的、发生的事情与情况。领域专家对数据库、网络接口或设计模式并不感兴趣,但关注业务领域中会发生的事实(动词),而领域事件就是捕获这些事实。
20世纪颇具影响力的哲学家维根斯坦说:世界是事实的总和,而非事物的总和。而传统哲学的观点是:世界是由存在着的万物组成的,他认为是世界是由发生着的事实组成的,这是他的《逻辑哲学论》中的基本出发点。**“存在着的万物”和“发生着的事实”两者的区别其实是主语和谓语动词的区别,“万物”通常是主语名词,而“事实”是一种动态的过程活动,涉及谓语动词。**从谓语动词中才能抽取出“万物对象”等名词的概念,他的逻辑分析观点认为:重要的不是对象,而是事实中对象之间的关系,对象所处的情势(事情的现实状况与发展趋势),对象只能在事实(对象之间的关系)中存在,离开事实的对象就毫无意义了。
在进行ER数据模型分析时,一般是先有实体(Entity)表,然后才表达关系(Relation),**维根斯坦逻辑思想的革命性在于:先有关系,才有实体,实体在关系中才有意义。**DDD中的领域事件、有界上下文和聚合都是这种关系的不同表达形式,或者说,数据库的关系表往往才是关注的重点,它是业务逻辑的关键所在。
在DDD建模领域,使用“领域事件”代表“事实”,表示对象之间的关系,既然领域事件表达了关系,需要涉及至少两个对象,领域事件通常也用于表达两个以上有界上下文之间的通信交流的方式。
事件代表过去发生的事件,它既是技术架构概念,也是业务概念。以事件为驱动的编程技术模型称为事件驱动架构(EDA),这里强调的是事件的业务概念。
一个事件代表某个已经发生的事实,在计算机系统中,事件是由一个对象表达,其包含有关事件的数据,比如发生的时间、地点等。这个事件对象可以存在于一个消息或数据库记录或其他组件的形式中,这样一个对象称为“一个事件”。
事件概念在业务系统中应用,诞生了领域事件和事件溯源等DDD实现方式:**通过引入事件,像引入服务概念一样,跨越业务和技术鸿沟,同时又能表达函数编程式思维。**在业务上将事件和DDD结合在一起,可以形成统一语言DSL。
领域事件的命名需要使用过去式,表示已经发生的事情。因为领域事件表示与领域相关的事件,不能简单说员工已创建、已删除,而是已雇用、已解雇、已退出。命名需要代表深刻的业务领域含义。
命名反例:
比较好的命名:
命令是事件的“触发器”,从UX(用户体验)设计角度看,命令体现了人的一种意图,因为有这个意图,所以才触发这个命令。命令进入领域系统后,系统是否响应执行这个命令,还是要依据领域自身的逻辑:如果逻辑允许,领域系统正确执行了这个命令,那么意味着系统内已经发生了一个事实,这个事实就是领域事件;如果违背领域逻辑,系统可以拒绝执行命令。
命令和事件的区别可以总结如下。
命令+事件的模式符合意图、执行和结果的范式,日常生活中这种方式很多,例如上级给下级命令,下级一定要执行,并返回结果。
命令在领域事件之前,是领域事件发生的因,但是在一个业务流程中,上一环节的事件可能直接变成下一环节的命令,这样就组成了一条因果链,通过这样的命令+事件建模分析,可以发现流程能力环节和有界上下文:
命令/事件只适合在发布订阅的有界上下文关系中。
事件风暴(EventStorming)是一种用于快速探索复杂业务领域的研讨会格式。
开一场事件风暴会议,领域专家、产品经理和技术人员等都参与其中,其中包括那些知道要问的问题(以及哪些人很想听听答案)以及知道答案的人,协调人必须使团队保持专注和参与,指导会议进展直到能完成完整的领域模型。
从领域事件开始探索领域,按时间线向前和向后发现领域事件,探索领域事件的起源。某些事件是用户操作的直接后果,因此使用不同颜色的便签区分命令和事件。
在便签上写上命令或事件的名称,贴到墙上或白板上,所以需要有足够大的墙面。如果讨论变得热烈,就需要固定前进的目标方向,以下是一些标准路线和方向。
如果发现大部分的相关信息暴露给了很多其他产品(有界上下文),这时可以抽象设计更通用的产品,主要针对使用者角色设计该产品,并公开一个更简单的服务;但是如果在整个业务平台找不到对应的使用者角色,则通过数据复制或重复知识方式实现不同产品或上下文之间的解耦。
总之,将有界上下文与产品和角色联系起来,能够从一个更高的高度也就是从业务知识层次去划分团队,提高团队生产效率。
“微服务就是有界上下文”是一种过分简化。微服务系统中存在四种有界上下文类型。
根据OASIS定义,**“服务”是一种允许访问一个或多个功能的机制,也就是提供操作功能以便于被访问。**比如“商品组件”这个术语不是一种服务,它的操作应该是“提供商品操作的功能”,可以表达为“商品服务组件”是较为准确的。