DDD 领域驱动的几种架构风格
在前面的几篇文章中,通过Spring Web应用的瑕疵引出改善的措施,我们讲解了领域驱动开发的相关概念和设计策略。本文主要讲解领域模型的几种类型和DDD的简单实践案例。
架构风格
在《实现领域驱动设计》一书中提到了几种架构风格:六边形架构、REST架构、CQRS 和事件驱动等。在实际使用中,落地的架构并非是纯粹其中的一种,而很有可能户将上述几种架构风格结合起来实现。
分层架构
分层架构的一个重要原则是每层只能与位于其下方的层发生耦合。分层架构可以简单分为两种,即严格分层架构和松散分层架构。在严格分层架构中,某层只能与位于其直接下方的层发生耦合,而在松散分层架构中,则允许某层与它的任意下方层发生耦合。DDD分层架构中比较经典的三种模式:四层架构、五层架构和六边形架构。
四层架构
Eric Evans在《领域驱动设计-软件核心复杂性应对之道》这本书中提出了传统的四层架构模式:
- User Interface为用户界面层(或表示层),负责向用户显示信息和解释用户命令。这里指的用户可以是另一个计算机系统,不一定是使用用户界面的人。
- Application为应用层,定义软件要完成的任务,并且指挥表达领域概念的对象来解决问题。这一层所负责的工作对业务来说意义重大,也是与其它系统的应用层进行交互的必要渠道。应用层要尽量简单,不包含业务规则或者知识,而只为下一层中的领域对象协调任务,分配工作,使它们互相协作。它没有反映业务情况的状态,但是却可以具有另外一种状态,为用户或程序显示某个任务的进度。
- Domain为领域层(或模型层),负责表达业务概念,业务状态信息以及业务规则。尽管保存业务状态的技术细节是由基础设施层实现的,但是反映业务情况的状态是由本层控制并且使用的。领域层是业务软件的核心,领域模型位于这一层。
- Infrastructure层为基础实施层,向其他层提供通用的技术能力:为应用层传递消息,为领域层提供持久化机制,为用户界面层绘制屏幕组件,等等。基础设施层还能够通过架构框架来支持四个层次间的交互模式。
传统的四层架构都是限定型松散分层架构,即Infrastructure层的任意上层都可以访问该层(“L”型),而其它层遵守严格分层架构。
五层架构
五层架构是根据《DCI架构:面向对象编程的新构想》中提及的DCI架构模式总结而成。DCI架构(Data、Context和Interactive三层架构):
- Data层描述系统有哪些领域概念及其之间的关系,该层专注于领域对象的确立和这些对象的生命周期管理及关系,让程序员站在对象的角度思考系统,从而让“系统是什么”更容易被理解。
- Context层:是尽可能薄的一层。Context往往被实现得无状态,只是找到合适的role,让role交互起来完成业务逻辑即可。但是简单并不代表不重要,显示化context层正是为人去理解软件业务流程提供切入点和主线。
- Interactive层主要体现在对role的建模,role是每个context中复杂的业务逻辑的真正执行者,体现“系统做什么”。role所做的是对行为进行建模,它联接了context和领域对象。由于系统的行为是复杂且多变的,role使得系统将稳定的领域模型层和多变的系统行为层进行了分离,由role专注于对系统行为进行建模。该层往往关注于系统的可扩展性,更加贴近于软件工程实践,在面向对象中更多的是以类的视角进行思考设计。
DCI目前广泛被看作是对DDD的一种发展和补充,用在基于面向对象的领域建模上。五层架构的具体定义如下:
- User Interface是用户接口层,主要用于处理用户发送的Restful请求和解析用户输入的配置文件等,并将信息传递给Application层的接口。
- Application层是应用层,负责多进程管理及调度、多线程管理及调度、多协程调度和维护业务实例的状态模型。当调度层收到用户接口层的请求后,委托Context层与本次业务相关的上下文进行处理。
- Context是环境层,以上下文为单位,将Domain层的领域对象cast成合适的role,让role交互起来完成业务逻辑。
- Domain层是领域层,定义领域模型,不仅包括领域对象及其之间关系的建模,还包括对象的角色role的显式建模。
- Infrastructure层是基础实施层,为其他层提供通用的技术能力:业务平台,编程框架,持久化机制,消息机制,第三方库的封装,通用算法,等等。
六边形架构
六边形架构(Hexagonal Architecture),又称为端口和适配器风格,最早由 Alistair Cockburn 提出。在 DDD 社区得到了发展和推广,之所以是六变形是为了突显这是个扁平的架构,每个边界的权重是相等的。
我们知道,经典分层架构分为三层(展现层、应用层、数据访问层),而对于六边形架构,可以分成另外的三层:
- 领域层(Domain Layer):最里面,纯粹的核心业务逻辑,一般不包含任何技术实现或引用。
- 端口层(Ports Layer):领域层之外,负责接收与用例相关的所有请求,这些请求负责在领域层中协调工作。端口层在端口内部作为领域层的边界,在端口外部则扮演了外部实体的角色。
- 适配器层(Adapters Layer):端口层之外,负责以某种格式接收输入、及产生输出。比如,对于 HTTP 用户请求,适配器会将转换为对领域层的调用,并将领域层传回的响应进行封送,通过 HTTP 传回调用客户端。在适配器层不存在领域逻辑,它的唯一职责就是在外部世界与领域层之间进行技术性的转换。适配器能够与端口的某个协议相关联并使用该端口,多个适配器可以使用同一个端口,在切换到某种新的用户界面时,可以让新界面与老界面同时使用相同的端口。
这样做的好处是将使业务边界更加清晰,从而获得更好的扩展性,除此之外,业务复杂度和技术复杂度分离,是 DDD 的重要基础,核心的领域层可以专注在业务逻辑而不用理会技术依赖,外部接口在被消费者调用的时候也不用去关心业务内部是如何实现。
REST架构
RESTful风格的架构将 资源
放在第一位,每个 资源
都有一个 URI 与之对应,可以将 资源
看着是 DDD 中的实体;RESTful 采用具有自描述功能的消息实现无状态通信,提高系统的可用性;至于 资源
的哪些属性可以公开出去,针对 资源
的操作,RESTful使用HTTP协议的已有方法来实现:GET、PUT、POST和DELETE。
在 DDD 的实现中,我们可以将对外的服务设计为 RESTful 风格的服务,将实体/值对象/领域服务作为资源
对外提供增删改查服务。但是并不建议直接将实体暴露在外,一来实体的某些隐私属性并不能对外暴露,二来某些资源获取场景并不是一个实体就能满足。因此我们在实际实践过程中,在领域模型上增加了 DTO 这样一个角色,DTO 可以组合多个实体/值对象的资源对外暴露。
CQRS
CQRS 就是平常大家在讲的读写分离,通常读写分离的目的是为了提高查询性能,同时达到读/写的解耦。让 DDD 和 CQRS 结合,我们可以分别对读和写建模,查询模型通常是一种非规范化数据模型,它并不反映领域行为,只是用于数据显示;命令模型执行领域行为,且在领域行为执行完成后,想办法通知到查询模型。
那么命令模型如何通知到查询模型呢? 如果查询模型和领域模型共享数据源,则可以省却这一步;如果没有共用数据源,则可以借助于 消息模式
(Messaging Patterns)通知到查询模型,从而达到最终一致性(Eventual Consistency)。
Martin 在 blog 中指出:CQRS 适用于极少数复杂的业务领域,如果不是很适合反而会增加复杂度;另一个适用场景为获取高性能的服务。
小结
本文主要介绍了几种架构风格:六边形架构、REST架构、CQRS 和事件驱动等。下面我们具体结合简单的实例来看其中的领域模型划分。
订阅最新文章,欢迎关注我的公众号:aoho求索
参考
- 点赞
- 收藏
- 关注作者
评论(0)