领域驱动设计阶段知识总结
领域
领域用来限定业务范围,范围就是边界,这就是DDD不断强调边界的原因。 DDD的领域就是这个边界内要解决的业务问题域。在研究和解决业务问题时,DDD会按照一定的规则将业务领域进行细分,当领域细分到一定程度后,DDD会将问题范围限定在特定的边界内,在这个边界内建立领域模型,进而用代码实现领域模型,解决相应业务问题。 业务范围可能也很大,如果把这个范围看成是一个空间的话,那它同时存在“问题空间”和“解决方案空间”。问题空间是从业务需求方面来看,而解决方案空间是从实现软件方面来看,两者有一些细微的区别的,最终用子域来划分问题空间,用限界上下文来划分解决方案空间。
子域
领域进一步划分为子域,每个子域对应一个更小的问题或更小的业务范围。 子域可以根据自身重要性和功能属性划分为三类子域,它们分别是:核心域、通用域和支撑域。 核心子域:业务成功的核心竞争力。 通用子域:不是核心,但被整个业务系统所使用 (可以直接购买); 支撑子域:不是核心,不被整个系统使用,完成业务的必要能力(可以外包出去)
限界上下文( Bounded Context )
在DDD建模和系统建设中有很多参与者,不同的参与者对同样的领域知识会有不同的理解,这样容易造成交流障碍,基于这个原因,DDD出现了通用语言和限界上下文两个概念。 通用语言定义上下文含义,限界上下文定义领域边界,保证每个上下文的含义在特定的边界内有唯一含义,领域模型则存在这个边界之内。 限界上下文确定了程序设计和拆分的方向。如果不考虑技术异构、团队沟通等外部因素,一个限界上下文理论上可以设计为一个模块或一个微服务。 限界上下文是定义领域边界的利器。 对限界上下文的通俗理解:用来封装通用语言和领域对象,提供上下文环境,保证在领域之内的一些术语、业务相关对象等(通用语言)有确切的含义,没有二义性。这个边界定义了模型的适用范围,使团队所有成员能够明确地知道什么应该在模型中实现,什么不应该在模型中实现。
通用语言(Ubiquitous Language )
通用语言是项目团队内部交流的统一语言,这个语言的语义环境由限界上下文限定,以确保语义唯一性。 通用语言包括术语和用例场景,并且能够直接反映在代码中,如商品、订单等对应实体对象,动词则表示一个动作或时间对应领域事件或命令。 通用语言贯穿DDD整个设计过程,基于它才能开发出可读性更好的代码,将业务需求转化为代码设计。
实体
领域模型中的实体是多个属性、操作或行为的载体。 实体以DO(领域对象)的形式存在,每个实体对象都有唯一的ID。 可以对一个实体对象进行多次修改,修改后的数据和原来的数据可能会大不相同,但是,它们拥有相同的ID,它们依然是同一个实体。 在DDD不同的设计过程中,实体形态是不同的。
值对象(Value Objects)
值对象是DDD领域模型中的一个基础对象,是若干属性的集合。 值对象多用于描述目的,具有整体概念和不可修改的属性。 值对象只有数据初始化操作和有限的不涉及修改数据的行为,基本不包含业务逻辑。 在领域建模过程中,值对象可以保证属性归类的清晰和概念的完整性,避免属性零碎
聚合(Aggregate)
聚合是一组相关对象的集合,把它作为数据修改的单元。每个聚合都有一个根和一个边界。边界定义了聚合的内部都有什么。根则是聚合所包含的一个特定实体。对聚合而言,外部对象只可以引用根,而边界内部的对象之间则可以互相引用。除根以外的其他实体都有本地标识,但这些标识只在聚合内部才需要加以区别,因为外部对象除了根实体之 外看不到其他对象。 聚合用来确保领域对象在实现共同的业务逻辑时,能保证数据一致性。 聚合有一个聚合根和上下文边界,边界根据业务单一职责和高内聚原则定义聚合内有哪些实体和对象,聚合之间是松耦合。 聚合在DDD分层结构中数据领域层,领域层包含多个聚合,共同实现业务逻辑。跨多个实体的业务逻辑通过领域服务来实现,跨多个聚合的业务逻辑通过应用服务来实现。
聚合根
聚合根作为聚合的管理者,在聚合内部负责协调实体和值对象按照固定的业务规则协同完成共同的业务逻辑。 聚合根的目的是避免由于复杂数据模型缺少统一的业务规则控制,而导致聚合、实体之间数据不一致性的问题。 在聚合之间,聚合根还是对外接口人,聚合根通过ID关联的方式接受外部任务和请求,在上下文内实现聚合之间的业务协同。 聚合之间通过聚合根ID关联引用,如果需要访问其他聚合的实体,就要先访问聚合根,再导航到聚合内部实体,外部对象不能直接访问聚合内实体。
领域事件(Domain Events)
DDD 领域建模通常采用事件风暴(Event Storming),它通常采用用例分析、场景分析和用户旅程分析等方法,通过头脑风暴列出所有可能的业务行为和事件,然后找出产生这些行为的领域对象,并梳理领域对象之间的关系,找出聚合根,找出与聚合根业务紧密关联的实体和值对象,再将聚合根、实体和值对象组合,构建聚合。 在事件风暴中,除了命令和操作等业务行为以外,还有一种非常重要的事件,这种事件发生后通常会导致进一步的业务操作,在 DDD 中这种事件被称为领域事件。 通过领域事件驱动的异步化机制,可以推动业务流程和数据在各个不同微服务之间的流转,实现微服务的解耦,减轻微服务之间服务调用的压力,提升用户体验。 领域事件处理包括:事件构建和发布、事件数据持久化、事件总线、消息中间件、事件接收和处理等。 领域事件,是解耦微服务的关键。
资源库(Repository)
只有聚合才拥有资源库,让客户始终聚焦于模型,通常聚合和资源库之间存在着一对一的关系。 资源库有两种实现风格:面向集合的资源库和面向持久化的资源库: 面向集合的资源库,接口跟集合接口设计类似,如提供add,addAll,remove,removeAll,需要持久化机制提供特殊支持; 面向持久化的资源库,接口风格和行为上会照顾持久化机制的实现,比如提供save,saveAll,remove,removeAll,重要是:对聚合实例的修改,需要显式的调用save方法,完成实例的持久化更新; 资源库和数据访问对象DAO区别: DAO是比资源库更低的一层, 主要是从数据库表的角度来看待问题,来提供CRUD操作; 资源库以“领域”为中心,所描述的是“领域语言”,资源库把ORM框架与领域模型隔离对外隐藏封装了数据访问机制。
领域服务(Domain Services)
领域服务定义:领域服务表示一个无状态的操作,用于实现特定于某个领域的任务。当某个操作不适合放在聚合(实体)或值对像上时,最好的方式便是使用领域服务。 正确区分领域服务(Domain Service)和应用服务(Application Service): 把业务类逻辑置于领域服务而不是应用服务,应用服务要做“薄”; 领域服务职责:跨聚合实例业务逻辑,没办法合理放到实体中的其它业务逻辑; 领域服务设计原则:用来组织业务逻辑,面向业务逻辑;细粒度;内部视图看系统;一个请求对应多个服务的多个方法;服务之间会存在依赖; 应用服务设计原则:用来封装业务逻辑;面向用例;粗粒度;外部视图看系统;一个请求对应一个方法;服务之间互不依赖; 领域服务和应用服务区分非常敏感,有时候需要在快速性/方便性上做折衷。