端口和适配器架构——DDD好帮手( 二 )


次适配器(别名Driven Adapter)实现应用的出口端口,向外部工具执行操作,例如

  • 向MySQL执行SQL,存储订单
  • 使用Elasticsearch的API搜索产品
  • 使用邮件/短信发送订单取消通知
若将其可视化,Driving Adapter和Driven Adapter基于端口围绕着应用形成左右结构,有别于传统的分层形象,形成一个六边形,因此也会称作六边形架构
端口和适配器架构——DDD好帮手

文章插图
 
可视化端口和适配器架构
如果到此我已经成功地把你讲晕了,请不要担心,我们接下来通过一个案例体验一下这个架构 。
端口和适配器架构有什么好处DDD邮轮,有咨询公司的报告显示,在接下来的几年内,邮轮游作为国人出游形式的比例会大幅上升,在这样一个大背景下,DDD Cruise,一家中国的邮轮公司,正在研发新一代的预订系统,尝试在线邮轮预订 。
目前计划中有两个触点应用:
  1. 微信小程序——提供邮轮搜索、邮轮预订的核心体验
  2. 中国区官网——这原是一个包含几个html页面的遗留应用,本次希望可以提供邮轮搜索的功能,值得注意的是,有部分邮轮是承包给旅行社销售,在网站上也需要展示以便做市场宣传

端口和适配器架构——DDD好帮手

文章插图
 
C4 Model——System Context Diagram
在这两个触点背后,是这次的主角,预订引擎1.0,计划以一个单体应用起步,为触点应用提供API,实现邮轮搜索、邮轮预订 。邮轮有多个数据来源,一部分来自一个遗留的预订系统,一部分来自业务部门的Excel表格,存放在AWS S3对象存储中 。最后还有一个小型的Headless CMS为市场人员提供邮轮描述,吸引眼球 。
现在让我们代入端口和适配器:
端口和适配器架构——DDD好帮手

文章插图
上“套路”,Driving Adapter一个,端口两个,Driven Adapter两个,连线少许
  1. API Controller是一个典型的Driving Adapter,它实现Rest API的Endpoint,调用入口端口CruiseSearch
  2. CruiseSearch作为应用的入口,向Driving Adapter屏蔽了邮轮搜索的实现 。
  3. 在另一边,出口端口CruiseSource要求返回全量的Cruise数据,为应用隐藏了外部数据源的集成方案:从遗留预订系统或AWS S3上的文件中抽取Cruise
促进单一职责原则
那么我们接下来在这个架构的基础上,进行概要设计,组件很自然地分为了三个部分:
端口和适配器架构——DDD好帮手

文章插图
概要设计类图
  1. 绿色是Driving Adapter,如果你对JAVA-Spring技术栈,可以从命名发现他是一个RestController
  2. 黄色是Cruise Search的实现,这里的概念只和邮轮相关,你在这里不应该看到技术术语
  3. 粉色部分则是Driven Adapter,除了与处理从数据源获取Cruise的Adapter,我们还需要
  4. a. CompositeCruiseSource,它不直接与数据源打交道,但它负责合并多个数据源并根据规则去除重复的Cruise
  5. b. CachingCruiseSource,它也不直接与数据源打交道,负责缓存Cruise
从架构角度来看,这些组件很简单 。请注意,简单(Simple)并不代表着容易(Easy),简单说的是只做一件事(或一种事),而容易是指做一件事的难度,例如如果使用Spring MVC实现Driving Adapter,利用注解寥寥几行代码就可以实现 。由于这些组件要么实现业务逻辑,要么实现对某种技术的适配,符合单一职责原则,你可以更有效地将变更控制在某一个范围内,更有信心地应对变化 。
澄清测试策略
应对变化的另一个有效手段是自动化测试,测试金字塔是最常被提及的测试策略,它建议自动化测试集应该由大量单元测试作为基础,它们编写容易、运行速度快,应该只包含少量的用UI驱动的测试,由于需要处理测试数据冲突、外部依赖准备,它们编写困难、运行速度也较慢 。但中层的service/集成测试的测试目标是什么,它们和单元测试有什么区别呢?
端口和适配器架构——DDD好帮手

文章插图
测试金字塔——端口和适配器版
如果你也有此困惑,不妨按照端口和适配器架构来重新解读,金字塔应该包含大量的Driving Adatper测试、业务逻辑测试、Driven Adapter测试 。
  1. Driving Adapter测试,目标是验证API能正确地解析输入、按预期的参数调用了入口端口并生成输出 。由于Driving Adapter不关心入口端口的实现,在测试中,可以通过Mock方便地构造测试场景,并提升测试速度 。


    推荐阅读