5. 菱形对称架构分析 <解构领域驱动设计>

前言

菱形对称架构出处来自张逸的<解构领域驱动设计>,这本书还是挺推荐的,不管是初入门还是已经已经有一些了解想深入领域驱动设计的同学都可以看一下。

其实有文章已经针对DDD 领域驱动战略篇 (6) 菱形对称架构 对菱形对称架构讲得比较清楚了,但这篇文章想结合之前对分层的分析再讨论一下菱形对称架构的特点。

菱形对称架构结合了端口和适配器架构,整洁架构,经典分层的思想,并改善了他们当中存在的问题,也有一定的创新性。

菱形对称架构总体分为3层,北向网关,领域层,南向网关。依赖关系也是从外到内。同样不用纠结北向和南向的命名,这只是为了好记,关键还是每层的作用是什么。领域层命名含义和前面的模型都没什么区别,我们接下来对南向网关和北向网关进行分析。

南向网关

端口和适配器

被动(出口)端口和被动(出口)适配器其实就是南向网关,前面也有提到,主动和被动适配器最好还是区分一下,一方面调用方向是不同的,一方面被动适配器需要依赖倒置,而主动适配器不用。不用依赖倒置其实也就不一定需要端口了,前面也提到入口端口的作用,但用处不是特别大。

所以这里主动和被动就不再被一起称为端口和适配器了,这个概念只用在被动适配器。

模型转换适配器

南向网关还有一个英文单词 ACL,其实就是防腐层,其实端口和适配器就是为了防腐的。前面讲端口和适配器的时候有提到2种适配器类型,包括隔离UI 和领域逻辑,隔离数据模型和领域模型。其实除了那两种之外结合 DDD 还有一种适配器,称为模型转换适配器。

模型转换是因为有2个模型,一个是领域层的模型,一个是三方的模型。例如有一个用户信息的通用服务和一个订单服务,订单服务需要对接用户信息服务。这时候订单服务可能直接使用用户信息服务的用户模型,但存在3个问题。

classDiagram class User { String id Int age Sex sex String phone Date birthday Date registerTime ..... }
  1. 被迫接受 User 作为命名。因为是通用服务,因此在用户信息服务命名为 User 是合理的,但在订单服务里面并不合理,订单服务里面应该称为 Buyer 会更合适。虽然在代码实现层面没什么大的区别,但对于业务表达来说不合适。
  2. 被迫接受 User 的所有属性。因为是通用服务,用户信息模型可能会有非常全的用户信息,但在订单服务里面其实并不关心。订单服务里面可能只关心用户id和手机号,强行耦合所有属性会让订单服务变得复杂和难以理解。
  3. User 提供的能力可能太全也可能不足。同样因为是通用服务,User 会提供很多通用能力,例如模糊查询用户,修改用户信息等等。但订单服务可能不需要这些能力,只需要一个根据用户id查询用户信息。或者用户信息服务无法提供根据手机号查询用户信息,但提供了根据手机号获取用户id,和根据用户id获取用户信息的接口,需要订单服务进行组合的。

这种情况下 DDD 推荐的做法是不同领域使用自己的模型,然后通过防腐层(适配器)进行模型的转换。订单服务可以定义自己的模型,例如下面的聚合和领域方法

classDiagram class Buyer { String id String phone }
java 复制代码
   // 这个类相当于端口,具体实现可能会通过 rpc 或者 http 来调用
   public class BuyerService{
       public Buyer getByPhone(String phone);
   }

端口是单独一层?

并不是,前面文章提到端口仍然属于领域层,只是为了突出端口的作用,才单独出来的。上下对称看着好看一点,但可能会造成误解。我觉得还是包在领域层会更合适一些。

北向网关

这一层应该和其他分层架构区别最大的一层了。

远程服务

其实远程服务对应的就是主动适配器了,为什么叫远程服务? 我们看看之前主动适配器有哪些类型。

看着都是和协议有关的。应用层是门面,对业务能力的体现。而主动适配器则是为了让业务能力能通过不同的协议去对外提供,例如 http,rpc,mq,schedule 等等。因此主动适配器更像是为了提供远程调用的能力而存在。能力比较单一,所以张逸老师直接把他命名成远程服务。更简单更容易理解。相反南向网关的适配器作用则多种多样,所以统称防腐层了。

本地服务

我们前面也有讲到其实从业务逻辑的放置上面,应用层和领域层之间的边界其实非常模糊,难以区分什么是应用逻辑和领域逻辑,所以按编排的角度去拆开应用层和领域层并不合适。但应用层又是必不可少的。因此张逸老师也是把应用层改称为本地服务。本地服务只是为了提供本地的能力,并不包含任何业务逻辑或者可能是编排的职责。

本地服务和远程服务的边界变得很清晰,一个进程内不同上下文只需要通过本地调用就行了,如果要拆成2个服务,那只需要改成远程服务调用就行了。

本地服务和领域层的边界也比较清晰,本地服务负责应用层模型的转换,充当系统门面和对请求进行通用处理就可以了。不用纠结是应用逻辑还是领域逻辑。

总结

菱形对称架构南向网关复用了端口和适配器的概念,而北向网关则针对主动适配器的特点改成了远程服务,解决了应用逻辑的概念解决应用层和领域层之间模糊的问题,让分层架构变得更加易于落地。是非常适合用于实战的分层架构。下面一篇文章也会针对菱形对称架构进行落地实践。

相关推荐
组合缺一2 小时前
Solon Cloud Gateway 开发:熟悉 ExContext 及相关接口
java·后端·gateway·solon
幸好我会魔法4 小时前
人格分裂(交互问答)-小白想懂Elasticsearch
大数据·spring boot·后端·elasticsearch·搜索引擎·全文检索
SomeB1oody5 小时前
【Rust自学】15.2. Deref trait Pt.1:什么是Deref、解引用运算符*与实现Deref trait
开发语言·后端·rust
何中应5 小时前
从管道符到Java编程
java·spring boot·后端
组合缺一6 小时前
Solon Cloud Gateway 开发:Route 的过滤器与定制
java·后端·gateway·reactor·solon
SomeB1oody6 小时前
【Rust自学】15.4. Drop trait:告别手动清理,释放即安全
开发语言·后端·rust
liruiqiang056 小时前
DDD-全面理解领域驱动设计中的各种“域”
开发语言·架构
customer086 小时前
【开源免费】基于SpringBoot+Vue.JS贸易行业crm系统(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·开源
花心蝴蝶.7 小时前
Spring IoC & DI
java·后端·spring
半夏知半秋8 小时前
rust学习-所有权
开发语言·后端·学习·rust