NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

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

评论 1 条评论

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

第七周作业

Geek_ac4080

第七周 性能优化 作业一

应鹏

极客大学架构师训练营

与前端训练营的日子--Week02

SamGo

学习

Week_07 作业

golangboy

极客大学架构师训练营

架构师训练营第 1 期 -Week7 - 性能优化一学习总结

鲁大江

极客大学架构师训练营

第 7 周 作业

Pyr0man1ac

Week 7 學習總結

Judyyy

week3- 请在草稿纸上手写一个单例模式的实现代码,拍照提交作业

未来已来

装饰器模式

猴子胖胖

设计模式 Go 语言

架构师训练营 1 期 - 第七周作业(vaik)

行之

极客大学架构师训练营

7.1性能测试:系统性能的主要技术指标

张荣召

第七周总结

Geek_ac4080

架构师训练营 1 期 - 第七周总结(vaik)

行之

极客大学架构师训练营

架构师训练营week07作业

IT老兵重开始

Week3 代码重构

贺志鹏

极客大学架构师训练营

架构师训练营第七周学习笔记

一马行千里

学习 极客大学架构师训练营

【架构师训练营第 1 期 07 周】 作业

Bear

极客大学架构师训练营

科技与产业革命的历史性交汇期,光华工程科技奖名单上的AI排面

脑极体

架构师训练营第一期 - 第七周学习总结

卖猪肉的大叔

极客大学架构师训练营

Week 7 作業

Judyyy

第 7 周 听说你有好几个线程

Pyr0man1ac

第3周作业

伊灵

springboot 热部署

hepingfly

Java springboot SpringCloud 热部署

第三周-总结

jizhi7

极客大学架构师训练营

架构师训练营第 1 期 -Week7 - 课后练习

鲁大江

极客大学架构师训练营

架构师训练营1期 - week07- 作业

lucian

极客大学架构师训练营

week3-作业二:根据当周学习情况,完成一篇学习总结

未来已来

第七周 性能优化 作业二

应鹏

极客大学架构师训练营

【架构师训练营第 1 期 07 周】 学习总结

Bear

极客大学架构师训练营

第三周-课后练习

jizhi7

#链表# #快慢指针#

玉皇大亮

链表 快慢指针

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