战略设计会控制和分解战术设计的边界与粒度;战术设计则从实证角度验证领域模型的有效性、完整性和一致性,进而以演进的方式对战略设计进行迭代。

在战略设计阶段,经过需求分析获得清晰的问题领域,通过对问题领域进行分析和建模,识别限界上下文,划分出相对独立的领域,并通过上下文映射建立它们之间的关系,辅以面向领域的应用架构模式划定领域与技术之间的界限。

战术设计阶段,深入限界上下文内对领域进行建模,并以领域模型指导应用设计与编码实现。在实现过程中,若发现领域模型存在重复、错位或丢失的情况,则需要对模型进行重构,甚至可以重新划分限界上下文,通过一套统一的领域模型,确保从领域需求分析到业务建模再到编码的一致性。

1.战略设计

① 找出实体和值对象等领域对象:根据场景分析,找出发起或者产生命令或领域事件的实体和值对象,将与实体或值对象有关的命令和事件聚集到实体上。

② 定义聚合:在定义聚合前,先找出聚合根,然后再找出与聚合根具有紧密依赖关系的实体和值对象。

③ 定义限界上下文:根据不同聚合的语义进行组织、分类,将具有相同语义的聚合归类到同一个限界上下文内。

传统的单体应用往往只有一个领域。而当对应用进行分布式的微服务化后,不同的微服务对应不同的问题领域,每个子领域中的模型都需要定义一个或多个限界上下文,在每个限界上下文中都有独立的领域模型。当使用多个领域模型时,限界上下文定义了每个模型自己的界限,而建模的过程可以确保每个模型的一致性和完整性。

限界上下文的核心理念是通过一套通用的语言为某个领域建模,而建模的核心是创建独立的数据库,用于存储、隔离领域数据。就如之前章节所讲的,微服务架构与SOA最大的区别在于数据隔离,微服务架构模式通过数据复制,而不是SOA中数据共享的方式来提供数据同步。换言之,微服务主导每个服务都拥有自己独立的数据库,也就是数据模型。通过 DDD 为微服务建模,从数据库的角度来看,也就是通过限界上下文来划分微服务的过程,划分之后为每个微服务进行领域建模,从而实现独立数据库的创建。

从微服务的角度来看,微服务必须被限制在一个领域模型也就是一个限界上下文中。此外,有不少场景一个限界上下文中有多个微服务,在此情况下微服务对应的最小单元是聚合。

2.战术设计

(1)服务识别和设计

针对微服务应用,服务表现了其对外提供的能力,它往往与DDD的应用服务或领域服务对应,可以将命令作为服务识别和设计的起点。具体步骤如下:

① 根据命令设计应用服务,确定应用服务的功能、服务集合、服务组合与编排方式。服务集合中的服务包括领域服务和其他外部的应用服务。

② 根据应用服务的功能要求设计和定义领域服务。应用服务可能是由多个聚合的领域服务组合而成的。

③ 根据领域服务的功能,确定领域服务内的实体及其功能,设计实体的基本属性和方法。另外,还需要考虑领域事件的异步化处理。

(2)梳理聚合中的对象

在确定了应用服务和领域服务之后,下一步就是需要细化每个聚合内的不同领域对象以及它们之间的关系。在整个梳理过程中,需要明确原先一些比较模糊的概念,例如确认是实体、值对象还是领域事件。更进一步,需要明确每个领域对象的具体属性,以及验证这些属性是否完整。

3.技术实现

用户接口层:负责向用户显示信息和解释用户指令。这里的用户可能是用户界面、Web服务或者其他如自动化测试和批处理脚本等业务。

应用层:应用层是很薄的一层,理论上不应该有业务逻辑,主要是面向用例和流程的相关操作。但应用层又位于领域层之上,因为领域层包含多个聚合,所以它可以协调多个聚合的服务和领域对象,完成服务组合与编排,协作完成业务操作。此外,应用层也是微服务之间交互的通道,它可以调用其他微服务的应用服务,完成微服务之间的服务组合、编排和通信。

领域层:该层的作用是实现核心业务逻辑,通过各种校验手段来保证业务的正确性。领域层主要体现领域模型的业务能力,用来表达业务概念、业务状态和业务规则。领域层包含聚合(实体和值对象)、领域服务等领域模型中的领域对象。当领域中的某些功能通过单一实体(或值对象)不能实现时,可以通过领域服务组合聚合内的多个实体(或值对象)来实现复杂的业务逻辑。

基础层:该层的作用是为其他各层提供通用的技术和基础服务,包括数据库、事件总线、API 网关、缓存、基础服务、第三方工具以及其他基础组件等。其比较常见的功能还是提供数据库持久化。基础层包含基础服务,它采用依赖倒置设计的原则来封装基础资源服务,实现应用层、领域层与基础层的解耦,降低外部资源变化对应用的影响。

 技术实现的具体步骤一般如下:

① 业务组件归类。业务组件由多个功能组件构成,功能组件可由聚合实现,从而确保事务的一致性。为了满足事务的一致性,对一组生命周期绑定的实体与值对象进行写入控制并保证强一致性的单元,叫作聚合。聚合对外只提供唯一的操作入口,该入口是一个实体,叫作聚合根。聚合只与写入操作有关,与读取操作无关。外部不可直接操作聚合内部成员,聚合根维护聚合内部成员关系的强一致性,聚合成员的生命周期小于或等于聚合根的生命周期,并由聚合根管理。

② API详细设计。选择合适的通信方式、API设计风格,利用可视化文档对API进行详细设计。

③ 对象类设计。参考领域模型的具体领域对象梳理结果,通常利用面向对象的语言设计具体的类。在确定各领域对象的属性后,可以设计各领域对象在代码模型中的代码对象(包括代码对象的包名、类名和方法名),建立领域对象与代码对象的一一映射关系。根据这种映射关系,相关人员可快速定位到业务逻辑所在的代码位置。

④ 数据库设计。在完成领域对象建模后,再考虑把需要持久化的对象保存在数据库中。数据库仅仅是一个保存数据的工具,不要把它过早地耦合在代码中。