发布在即!企业 AIGC 应用程度测评,3 步定制专属评估报告。抢首批测评权益>>> 了解详情
写点什么

阿里技术专家详解 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:084393

评论 1 条评论

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

重点人员动态管控系统开发,智慧公安情报研判系统搭建

电微13828808271

TypeScript 针对 JavaScript 做了什么

HoneyMoose

一文帮你掌握TDengine的降采样查询+跨时区统计

TDengine

数据库 tdengine 后端

企业采购管理的这些痛点,如何解决?

低代码小观

企业管理 管理系统 管理工具 采购管理 企业采购管理

【Promise 源码学习】第八篇 - 完善 Promise 并通过 promise-aplus-tests 测试

Brave

源码 Promise 11月日更

Selenium修改HTTP请求头三种方式

FunTester

测试 HTTP selenium FunTester UI自动化

TDSQL | 多类型数据库统一管理,腾讯云数据库DBhouse工具重磅发布

腾讯云数据库

数据库 tdsql

国家质量基础设施(NQI)一站式综合服务平台开发搭建

电微13828808271

三分钟永久激活WebStorm、PHPStorm、PyCharm、IntelliJ IDEA等JetBrains系列IDE

echeverra

pycharm webstorm IntelliJ IDEA PHPStorm

架构实战营模块八作业

Geek_d18264

架构实战营

云管理软件哪家好?有哪些功能?咨询电话多少?

行云管家

云计算 云服务 云平台 云资源 云管理

模块三作业

Geek_1d37ea

架构训练营

新机遇,拨开证劵企业生态转型迷雾

大咖说

云计算 阿里云 数字化转型 数字化 企业上云

Moment.js 转换 UTC 格式的 2 个小问题

HoneyMoose

云小课 | DSC:快速识别敏感数据并脱敏

华为云开发者联盟

华为云 识别 数据脱敏 数据安全中心 敏感数据

Apache Pulsar 荣获中国开源云联盟「2021 优秀开源项目」

Apache Pulsar

大数据 云原生 开源项目 Apache Pulsar 消息系统 Apache Pulsar 社区

OPPO 图数据库平台建设及业务落地

NebulaGraph

图数据库 知识图谱 图数据库实战 分布式图数据库

100G云服务器诞生记

科技热闻

新时代下如何构建TDSQL-C数据库产品

腾讯云数据库

数据库 tdsql

8大原则带你秒懂Happens-Before原则

华为云开发者联盟

线程 并发 Happens-Bfore Java内存

金融级数据库新坐标:腾讯云TDSQL发布全自研新敏态引擎

腾讯云数据库

数据库 tdsql

校招 C++ 大概学习到什么程度?

博文视点Broadview

一定要过等保吗?过了等保是不是非常安全?

行云管家

网络安全 等保 堡垒机 等级保护

Vue进阶(幺捌贰):父子组件元素获取、方法互相调用

No Silver Bullet

Vue 组件通信 11月日更

智联招聘的Web模块扩展落地方案

智联大前端

组件化 SSR

在SAP云平台的CloudFoundry环境下消费ABAP On-Premise OData服务

Jerry Wang

SAP abap CloudFoundry 11月日更

JDK的第三个LTS版本JDK17来了

程序那些事

程序那些事 11月日更 jdk17 java17

【重磅官宣】UDC2022:解码Z世代、力造科技潮生活

科技热闻

10行代码,撸一个在线个人简历页面!

老表

Python GitHub Linux web开发 跟老表学云服务器

用一个极致简单的场景演练领域建模

神帅

DDD 领域建模

ABAP Netweaver和git的快捷方式

Jerry Wang

SAP Netweaver CloudFoundry 11月日更

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