领域驱动架构篇—菱形对称架构
领域驱动设计中,对于架构风格有一个指导思想:不同的限界上下文,根据其领域模型和业务特征,可以选用不同的架构风格。
在传统的分层架构与领域驱动理念相结合的过程中,产生了多种架构风格:六边型架构、整洁架构、微服务架构等。本文是根据对对IT文艺工作者张逸老师的相关Chat阅读和思考整理而得。
前言
菱形对称架构(Rhombic Symmetric Architecture) 主要针对领域层次的架构,借鉴了六边形架构、分层架构、整洁架构的知识,并结合了领域驱动设计的元模型,使其能够更好地运用到限界上下文的架构设计中。
架构演进之路
六边型架构
六边型架构的特点就是:在满足整洁架构思想的同时,更加关注内层与外层自己与外部资源之间的通信本质
对于外界每种类型都有一个适配器与之对应:消息适配器,REST适配器,SOAP适配器,持久化适配器
通过内外两个六边形的不同边界清晰地展现了了领域与技术的边界(蓝色是领域,灰色是技术)
- 端口:我们可以把端口看成是具体的协议
- 适配器:看成是具体的通信垂直技术,如:Servlet、REST、JPA
- 应用:用例级别的业务逻辑,体现了一个业务场景
- 领域模型:系统级别业务逻辑,多个服务和聚合构成一个用例
六边型架构中,端口是解耦的关键:入口隔离了外部请求和技术实现细节;出口隔离了数据持久化和外部访问设备
六边型架构的业务(预定机票)示例:
- ReservationResource:订票请求发送给以 RESTful 契约定义的资源服务,它作为入口适配器,介于应用六边形与领域六边形的边界之内,在接收到以 JSON 格式传递的前端请求后,将其转换(反序列化)为入口端口ReservationAppService需要的请求对象
- ReservationAppService:入口端口为应用服务,位于领域六边形的边界之上。当它在接收到入口适配器转换后的请求对象后,调用位于领域六边形边界内的领域服务TicketReservation
- TicketReservation:订票的领域逻辑
- ReservationRepository:出口端口为资源库,位于领域六边形的边界之上,定义为接口
- ReservationRepositoryAdapter:真正访问数据库的逻辑则由介于应用六边形与领域六边形边界内的出口适配器,该实现访问了数据库,将端口发送过来的插入订票记录的请求转换为数据库能够接收的消息,执行插入操作
整洁架构
整洁架构的模型是一个类似内核模式的内外层结构,它具有以下特点:
- 1 越靠近中心,层次越高
- 2 由外到内:框架与驱动程序 --》接口适配器 --》应用级业务逻辑--》系统级业务逻辑
- 3 源码中依赖关系必须指向同心圆内层,从外到内
我们可以在整洁架构上得到很多值得回味的设计思想:
- 不同层次的组件其变化频率也不相同,引起变化的原因也不相同(符合高内聚低耦合设计思想)
- 层次越靠内组件依赖越少,处于核心业务实体没有任何依赖(领域模型和技术完美解耦)
- 层次越靠内组件与业务关系越密切,专属于特定领域的内容,很难形成通用的框架(领域构建出了壁垒)
在这个架构中,外层圆代表的是机制,内层圆代表的是策略;机制和具体的技术实现有关,容易受到外部环境变化;策略与业务有关,封装了最核心领域模型,最不容易受到外界环境变化的影响
六边形架构仅仅区分了内外边界,提炼了端口与适配器角色,并没有规划限界上下文内部各个层次与各个对象之间的关系;而整洁架构又过于通用,提炼的是企业系统架构设计的基本规则与主题。因此,当我们将六边形架构与整洁架构思想引入到领域驱动设计的限界上下文时,还需要引入分层架构给出更为细致的设计指导,即确定层、模块与角色构造型之间的关系。
经典分层架构
分层架构是运用最为广泛的架构模式,几乎每个软件系统都需要通过层(Layer)来隔离不同的关注点(Concern Point),以此应对不同需求的变化,使得这种变化可以独立进行。
- 用户界面层:负责向用户展现信息和解释用户命令。包含web端UI界面、移动端UI界面、第三方服务等。
- 应用层:很薄的一层,用来协调应用的活动,它不包含业务逻辑,它不保留业务对象的状态。在领域设计中,它其实是一个外观(Facade),一般是供其他界限上下文基础设置层中controller调用。
- 领域层:本层包含领域的信息,是业务软件的核心所在。在这里保留业务对象的状态,对业务对象状态的持久化被委托给基础设置层。它包含领域设计中的:聚合、值对象、实体、服务、资源接口等。
- 基础设置层:不要简单的理解为物理上的一层,它作为其他层的支撑库而存在。它提供了层间的通讯,实现对业务对象的持久化。一般包含:网络通讯、资源实现(数据库持久化实现)、异步消息服务、网关、controller控制等。
菱形对称架构
菱形对称架构融合了分层架构和六边型架构的思想。
对六边型进行分层映射
- 入口适配器:响应边界外客户端的请求,需要实现进程间通信以及消息的序列化和反序列化,这些功能皆与具体的通信技术有关,故而映射到基础设施层
- 入口端口:负责协调外部客户端请求与内部应用程序之间的交互,恰好与应用层的协调能力相配,故而映射到应用层
应用程序:承担了整个限界上下文的领域逻辑,包含了当前限界上下文的领域模型,毫无疑问,应该映射到领域层 - 出口端口:作为一个抽象的接口,封装了对外部设备和数据库的访问,由于它会被应用程序调用,遵循整洁架构思想,也应该映射到领域层
- 出口适配器:访问外部设备和数据库的真正实现,与具体的技术实现有关,映射到基础设施层
突破分层架构
分层架构仅仅是对限界上下文的逻辑划分,在编码实现时,逻辑层或许会以模块的形式表现,但是也可能将整个限界上下文作为一个模块,每个层不过是命名空间的差异,定义为模块内的一个包。不管是物理分离的模块,还是逻辑分离的包,只要能保证限界上下文在六边形边界的保护下能够维持内部结构的清晰,就能降低架构腐蚀的风险。
依据整洁架构遵循的“稳定依赖原则”,领域层不能依赖于外层。因此,出口端口只能放在领域层。事实上,领域驱动设计也是如此要求的,它在领域模型中定义了资源库(Repository),用于管理聚合的生命周期,同时,它也将作为抽象的访问外部数据库的出口端口。
将资源库放在领域层确有论据佐证,毕竟,在抹掉数据库技术的实现细节后,资源库的接口方法就是对聚合领域模型对象的管理,包括查询、修改、增加与删除行为,这些行为也可视为领域逻辑的一部分。
然而,限界上下文可能不仅限于访问数据库,还可能访问同样属于外部设备的文件、网络与消息队列。为了隔离领域模型与外部设备,同样需要为它们定义抽象的出口端口,这些出口端口该放在哪里呢?如果依然放在领域层,就很难自圆其说。例如,出口端口EventPublisher支持将事件消息发布到消息队列,要将这样的接口放在领域层,就显得不伦不类了。倘若不放在位于内部核心的领域层,就只能放在领域层外部,这又违背了整洁架构思想。
既然出口端口的位置如此尴尬,而且很明显出和入不太对称,所以我们干脆就把出和入对称下,将端口和适配器统一掉,组合成“网关”。
上面的对称架构虽脱胎于六边形架构与领域驱动设计分层架构,却又有别于二者。
对称架构北向网关定义的远程网关与本地网关同时承担了端口与适配器的职责,这实际上改变了六边形架构端口-适配器的风格;领域层与南北网关层的内外分层结构,以及南向网关规定的端口与适配器的分离,又与领域驱动设计的分层架构渐行渐远。
既然已经改变,就根据思想,重新抽象下架构图
就得到了菱形对称架构,主要体现了南北网关的对称关系
菱形对称架构的组成
菱形架构其上下文的组成:
- 北向网关的远程网关:进程间通信
- 北向网关的本地网关:进程内通信
- 领域层的领域模型:领域逻辑
- 南向网关的端口抽象:各种资源库操作的抽象借接口,可以被领域层依赖,
- 南向网关的适配器实现:网关端口的实现,运行时通过依赖注入将适配器实现注入到领域层
以六边型中的业务示例为例,改成菱形架构的话,其架构如下:
引入上下文映射
北向网关演变
北向网关和开放式主机服务很类似,但是职能更多,相当于开放式主机层,包含了远程服务和本地服务
- 远程服务:跨进程通信;包含资源(Resource)服务、供应者(Provider)服务、控制器(Controller)服务与事件订阅者(Event Subscriber)服务
- 本地服务:进程内通信;对应于应用层的应用服务
当外部请求从远程服务进入时,如果需要调用领域层的领域逻辑,则必须经由本地服务发起对领域层的请求。此时的本地服务又扮演了端口的作用,可认为远程服务是本地服务的客户端。
南向网关演变
南向网关引入了抽象的端口来隔离内部领域模型对外部环境的访问,这一价值很等同于上下文映射中的防腐层,同样它也扩大了防腐层的功能
南向网关的端口分为:
- 资源库(repository)端口:隔离对外部数据库的访问,对应的适配器提供聚合的持久化能力
- 客户端(client)端口:隔离对上游限界上下文或第三方服务的访问,对应的适配器提供对服务的调用能力
- 事件发布者(event publisher)端口:隔离对外部消息队列的访问,对应的适配器提供发布事件消息的能力
改进后的菱形对称架构
菱形对称架构去掉了应用层和基础设施层的概念,以统一的网关层进行概括,并以北向与南向分别体现了来自不同方向的请求。如此形成的对称结构突出了领域模型的核心作用,更加清晰地体现了业务逻辑、技术功能与外部环境之间的边界。
资源库视为防腐层的端口与适配器,作为领域建模时的角色构造型,与场景驱动设计更好地结合,增强了领域模型的稳定性。应用层被去掉之后,被弱化为开放主机服务层的本地服务,相当于从设计层面回归到服务外观的本质,也有助于解决应用服务与领域服务之间的概念之争。
代码模型示例:
ohs 为开放主机服务模式的缩写,acl 是防腐层模式的缩写,pl 代表了发布语言;也可以使用北向(northbound)与南向(sourthbound)取代 ohs 与 acl 作为包名,使用消息(messages)契约取代 pl 的包名。
转载地址:DDD领域驱动战略篇(6) 菱形对称架构
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。