1.2.3 领域驱动战术设计
请注意,在DDD的战略设计中,统一语言属于问题空间的范畴,而限界上下文属于解空间的范畴。也就是说,通过战略设计,我们已经完成了从问题空间到解空间的映射。但是,这时的解空间只是一个框架,需要通过DDD的战术设计进行填充。DDD战术设计与业务模型的对应关系如图1-25所示。
图1-25 业务模型与战术设计的对应关系
1.领域对象
在传统软件开发中,业务是由数据驱动的,开发人员从数据的角度来规划对象的组织形式,并以面向数据库的方式对这些数据对象进行设计和建模,每个业务对象只包含业务数据和结构的定义,并不具备业务操作能力,这就是所谓的贫血模型(Anaemic Model)。
虽然以数据作为主要关注点的开发模式也能完成对系统的构建,但我们认为面向领域的模型对象才是能够表达统一语言的有效载体。究其原因,在于很多对象并不能简单地通过它们的数据属性来定义,而是应该具有一系列的标识和行为定义。在DDD中,领域模型对象包括三大类,如图1-26所示。
图1-26 领域模型对象的三大类
在图1-26中,有聚合、实体和值对象3类领域对象,我们认为这些领域对象才是能够表达统一语言的有效载体。
● 聚合。聚合的核心思想在于简化对象之间的关联关系,一个聚合内部的所有对象只能通过聚合对象来进行访问,从而有效降低了对象之间的交互复杂度。
● 实体。实体是聚合内部具有唯一标识的一种业务对象,具有丰富的操作行为、状态可变性,以及完整生命周期。
● 值对象。值对象有点类似贫血模型对象,只关心对象的数据属性而不具备操作行为。值对象是不变对象、没有唯一标识且通常不包含业务逻辑。
2.领域服务
我们可以将业务模型中的业务逻辑抽象为一组业务规则。业务规则从概念上说通常不属于任何一个独立的对象,而是涉及一组领域模型对象之间的交互和操作。当领域模型中某个重要操作无法由单个聚合或实体完成时,应该为模型添加一个独立的访问入口,这就是领域服务(Domain Service),如图1-27所示。
图1-27 领域服务的定位
从图1-27可以看出,领域服务的构建涉及多个领域模型对象之间的交互和协作,这是单个领域模型对象所不能完成的操作。
3.领域事件
现实中很多场景可以抽象为事件,如某一个操作发生时发送一条消息、出现了某种情况执行某个既定业务操作等。本质上,事件代表的是一种业务状态的变化,是一种独立的建模对象,在DDD中被称为领域事件(Domain Event),如图1-28所示。
图1-28 领域事件的执行流程
领域事件实质上就是将领域中发生的活动建模为一系列离散事件。领域事件也是一种领域对象,是领域模型的重要组成部分。
4.资源库
对于任何一个系统,业务数据都需要进行统一的管理和维护,开发人员应将数据保存到各种数据持久化媒介中。我们认为系统中应该存在一个专门针对数据访问的入口,通过该入口可以对所有领域模型对象进行遍历。在DDD中,资源库(Repository)实际上充当了领域模型对象提供者的角色,如图1-29所示。
图1-29 资源库的定位
简单来说,资源库用于实现对业务数据的持久化管理,同时帮助开发人员屏蔽数据访问过程中的技术复杂性。
5.应用服务
DDD中的应用服务(Application Service)提供了类似数据传输对象(Data Transfer Object,DTO)模式和外观模式(Facade Pattern)的功能。我们希望为系统中的一组接口提供一个一致的界面,从而使其更易用,这就是应用服务的价值,如图1-30所示。
图1-30 应用服务的定位
对于应用服务,我们不应该放置任何与业务逻辑相关的操作,而是仅完成来自用户界面或外部系统的集成需求,所以是很薄的一层技术组件。
到此,我们完成了从业务模型到DDD方法的完整映射,读者可以结合图1-31中的内容进行总结和回顾。
图1-31 业务模型与DDD之间的映射关系
请注意,图1-31中DDD的各个组件并不是位于同一层次的,各个限界上下文都应该包括战术设计的所有技术组件,如图1-32所示。
图1-32 限界上下文和战术设计技术组件示意图