写点什么

阿里技术专家详解 DDD 系列 第二弹 - 应用架构 (三)

  • 2019-12-18
  • 本文字数:2406 字

    阅读完需:约 8 分钟

阿里技术专家详解DDD系列 第二弹 - 应用架构(三)

3 DDD 的六边形架构

在我们传统的代码里,我们一般都很注重每个外部依赖的实现细节和规范,但是今天我们需要敢于抛弃掉原有的理念,重新审视代码结构。在上面重构的代码里,如果抛弃掉所有 Repository、ACL、Producer 等的具体实现细节,我们会发现每一个对外部的抽象类其实就是输入或输出,类似于计算机系统中的 I/O 节点。这个观点在 CQRS 架构中也同样适用,将所有接口分为 Command(输入)和 Query(输出)两种。除了 I/O 之外其他的内部逻辑,就是应用业务的核心逻辑。基于这个基础,Alistair Cockburn 在 2005 年提出了 Hexagonal Architecture(六边形架构),又被称之为 Ports and Adapters(端口和适配器架构)。



image.png


在这张图中:


  • I/O 的具体实现在模型的最外层

  • 每个 I/O 的适配器在灰色地带

  • 每个 Hex 的边是一个端口

  • Hex 的中央是应用的核心领域模型


在 Hex 中,架构的组织关系第一次变成了一个二维的内外关系,而不是传统一维的上下关系。同时在 Hex 架构中我们第一次发现 UI 层、DB 层、和各种中间件层实际上是没有本质上区别的,都只是数据的输入和输出,而不是在传统架构中的最上层和最下层。


除了 2005 年的 Hex 架构,2008 年 Jeffery Palermo 的 Onion Architecture(洋葱架构)和 2017 年 Robert Martin 的 Clean Architecture(干净架构),都是极为类似的思想。除了命名不一样、切入点不一样之外,其他的整体架构都是基于一个二维的内外关系。这也说明了基于 DDD 的架构最终的形态都是类似的。Herberto Graca 有一个很全面的图包含了绝大部分现实中的端口类,值得借鉴。



image.png


3.1 - 代码组织结构


为了有效的组织代码结构,避免下层代码依赖到上层实现的情况,在 Java 中我们可以通过 POM Module 和 POM 依赖来处理相互的关系。通过 Spring/SpringBoot 的容器来解决运行时动态注入具体实现的依赖的问题。一个简单的依赖关系图如下:




image.png


image.png

3.1.1 - Types 模块

Types 模块是保存可以对外暴露的 Domain Primitives 的地方。Domain Primitives 因为是无状态的逻辑,可以对外暴露,所以经常被包含在对外的 API 接口中,需要单独成为模块。Types 模块不依赖任何类库,纯 POJO 。



image.png

3.1.2 - Domain 模块

Domain 模块是核心业务逻辑的集中地,包含有状态的 Entity、领域服务 Domain Service、以及各种外部依赖的接口类(如 Repository、ACL、中间件等。Domain 模块仅依赖 Types 模块,也是纯 POJO 。



image.png

3.1.3 - Application 模块

Application 模块主要包含 Application Service 和一些相关的类。Application 模块依赖 Domain 模块。还是不依赖任何框架,纯 POJO。



image.png

3.1.4 - Infrastructure 模块

Infrastructure 模块包含了 Persistence、Messaging、External 等模块。比如:Persistence 模块包含数据库 DAO 的实现,包含 Data Object、ORM Mapper、Entity 到 DO 的转化类等。Persistence 模块要依赖具体的 ORM 类库,比如 MyBatis。如果需要用 Spring-Mybatis 提供的注解方案,则需要依赖 Spring。



image.png

3.1.5 - Web 模块

Web 模块包含 Controller 等相关代码。如果用 SpringMVC 则需要依赖 Spring。



image.png

3.1.6 - Start 模块

Start 模块是 SpringBoot 的启动类。

3.2 - 测试

Types,Domain 模块都属于无外部依赖的纯 POJO,基本上都可以 100%的被单元测试覆盖。


Application 模块的代码依赖外部抽象类,需要通过测试框架去 Mock 所有外部依赖,但仍然可以 100%被单元测试。


Infrastructure 的每个模块的代码相对独立,接口数量比较少,相对比较容易写单测。但是由于依赖了外部 I/O,速度上不可能很快,但好在模块的变动不会很频繁,属于一劳永逸。


Web 模块有两种测试方法:通过 Spring 的 MockMVC 测试,或者通过 HttpClient 调用接口测试。但是在测试时最好把 Controller 依赖的服务类都 Mock 掉。一般来说当你把 Controller 的逻辑都后置到 Application Service 中时,Controller 的逻辑变得极为简单,很容易 100%覆盖。


Start 模块:通常应用的集成测试写在 start 里。当其他模块的单元测试都能 100%覆盖后,集成测试用来验证整体链路的真实性。

3.3 - 代码的演进/变化速度

在传统架构中,代码从上到下的变化速度基本上是一致的,改个需求需要从接口、到业务逻辑、到数据库全量变更,而第三方变更可能会导致整个代码的重写。但是在 DDD 中不同模块的代码的演进速度是不一样的:


Domain 层属于核心业务逻辑,属于经常被修改的地方。比如:原来不需要扣手续费,现在需要了之类的。通过 Entity 能够解决基于单个对象的逻辑变更,通过 Domain Service 解决多个对象间的业务逻辑变更。


Application 层属于 Use Case(业务用例)。业务用例一般都是描述比较大方向的需求,接口相对稳定,特别是对外的接口一般不会频繁变更。添加业务用例可以通过新增 Application Service 或者新增接口实现功能的扩展。


Infrastructure 层属于最低频变更的。一般这个层的模块只有在外部依赖变更了之后才会跟着升级,而外部依赖的变更频率一般远低于业务逻辑的变更频率。


所以在 DDD 架构中,能明显看出越外层的代码越稳定,越内层的代码演进越快,真正体现了领域“驱动”的核心思想。

4 总结

DDD 不是一个什么特殊的架构,而是任何传统代码经过合理的重构之后最终一定会抵达的终点。DDD 的架构能够有效的解决传统架构中的问题:


高可维护性:当外部依赖变更时,内部代码只用变更跟外部对接的模块,其他业务逻辑不变。


高可扩展性:做新功能时,绝大部分的代码都能复用,仅需要增加核心业务逻辑即可。


高可测试性:每个拆分出来的模块都符合单一性原则,绝大部分不依赖框架,可以快速的单元测试,做到 100%覆盖。


代码结构清晰:通过 POM module 可以解决模块间的依赖关系, 所有外接模块都可以单独独立成 Jar 包被复用。当团队形成规范后,可以快速的定位到相关代码。


本文转载自淘系技术公众号。


原文链接:https://mp.weixin.qq.com/s/MU1rqpQ1aA1p7OtXqVVwxQ


2019-12-18 16:084731

评论 1 条评论

发布
用户头像
了不起的文章
2019-12-21 14:35
回复
没有更多了
发现更多内容

实用机器学习笔记十二:线性模型

打工人!

AI 算法 学习笔记 机器学习算法 12月日更

腾讯云容器安全已支持检测Apache Log4j2漏洞

腾讯安全云鼎实验室

腾讯云安全、漏洞分析

聚集索引数据写入

lixiaofeng

索引优化 签约计划第二季

首个国产元宇宙“希壤”来临,百度成了先锋!

CECBC

XML数字签名原理篇

RingCentral铃盛

ZooKeeper 分布式锁 Curator 源码 05:分布式读写锁和联锁

程序员小航

Java 分布式锁 curator

一篇带你用 VuePress + Github Pages 搭建博客

冴羽

JavaScript 自己搭建博客网站 Vue 博客 vuepress

Camtasia的字幕功能应当如何使用

淋雨

Camtasia 录屏软件

MSSQL 执行计划

lixiaofeng

SQL优化 签约计划第二季

【HZERO微服务平台5】JHipster接入HZERO

qiaoxingxing

签约计划第二季

全新“支付方式”来了,数字货币六大行公开支持,安全且转账免费

CECBC

java实现数据导出

卢卡多多

28天写作 数据导出 12月日更

深入研究【HZERO微服务平台】开源免费的企业级PaaS平台

qiaoxingxing

内容合集 签约计划第二季

谈跨域资源共享之CORS

devpoint

jsonp CORS 12月日更

如何优雅地在Vue页面中引入img图片

CRMEB

降低成本对技术组织意味着什么?

田晓亮

云计算 serverles

3天后,浪潮云有大动作!

Hoo虎符研究院 | 币圈后浪 ——DVD

区块链前沿News

Hoo 币圈后浪 项目分析

2021年终总结:如何快速学完多个课程

石云升

知识付费 年终总结 28天写作 12月日更

Volatile 原理五:禁止指令重排是什么?

悟空聊架构

volatile 28天写作 悟空聊架构 12月日更 禁止指令重排

MSSQL索引优化原理

lixiaofeng

SQL优化 签约计划第二季

2021阿里云弹性计算年度峰会将于12月21日开启线上直播,五大看点不容错过

阿里云弹性计算

弹性计算 年度峰会 线上直播

TDengine在同花顺组合管理业务中的优化实践

TDengine

数据库 tdengine 时序数据库

“朋友圈”又添好友,DataPipeline与统信服务器操作系统完成产品互认证

DataPipeline数见科技

大数据 操作系统 中间件 数据同步 数据融合

为什么非要做个app

张老蔫

28天写作

XML数字签名实践篇

RingCentral铃盛

区块链产业规模约达50亿元,已成为数字经济发展重要引擎

CECBC

OAuth2.0面面观

RingCentral铃盛

【行业观察】AI数据标注行业

mtfelix

28天写作

实体链接在小布助手和OGraph的实践应用

安第斯智能云

人工智能 深度学习 算法 nlp 知识图谱

Hoo虎符研究院 | 区块链简报 20211213期

区块链前沿News

区块链 Hoo 虎符交易所

阿里技术专家详解DDD系列 第二弹 - 应用架构(三)_语言 & 开发_淘系技术_InfoQ精选文章