DDD 领域驱动设计与六边形架构

Published Fri 04 August 2017 in 软件工程

by HanXiao  

软件世界是对现实世界的抽象, 而传统的三层架构或 MVC 架构 (参考: 三层架构与 MVC), 却是结构化的生搬硬套, 在这些架构中的设计出来的对象是贫血的, 是“业务逻辑”类, 跟现实世界甚至毫无关系, 那么根据根据现实世界的真实领域, 映射到软件世界的领域驱动设计六边形架构, 才是更好的架构方式, 应用场景更广, 扩展更简单.

PS, 领域驱动设计继承了职责驱动设计, 或者可以说是职责驱动设计的进化.

领域驱动设计不是一种设计风格, 也不是一种架构模型, 而是一种思考方式, 指导如何进行职责的划分, 而六边开架构就是一种架构了, “六”只是一个量词, 表明这种架构可以支持多个客户端, 它也被称之为端口与适配器架构, 很适合和领域驱动结合 (在《实现领域驱动设计》一书中作者将六边形架构应用于 DDD).

在六边形架构中, 提出了一种具有对称性特征的架构风格. 在这种架构中, 不同的客户通过”平等”的方式与系统交互. 包括输入系统 (web,gui) 和输出系统 (database、log、message queue). 需要新的客户吗? 不是问题. 只需要添加一个新的适配器将客户输入转化成能被系统 API 所理解的参数就行了.

对于输出系统, 为了不产生反依赖, 可以对其适配器使用观察者模式.

六边形架构的核心是领域层, 这个需要看完《领域驱动设计》才能有深刻理解, 我现在正在读;

外界与领域层的交互都通过应用层完成, 应用层是领域层的直接客户 (可有说叫端口层, 想要表达的含义是一样的). 应用层中不应该包含有业务逻辑, 否则就造成了领域逻辑的泄漏, 而应该是很薄的一层, 主要起到协调的作用, 它所做的只是将业务操作代理给我们的领域层, 扮演了系统门面/外观 (Facade) 的角色.

应用层的本质是《UML与模式应用》中提到 GRASP 中的控制器, 可以参考 《UML与模式应用》 P218、412, 但六边形架构推荐依据用例来创建控制器.

控制器模式体现模型-视图分离原则 (这里的模型指的是领域层对象, 而不是 MVC 中的 Model), 防止 UI 层与过多的领域层对象发生耦合, 同时也避免在 UI 层混入应用逻辑.

控制器可以是真实领域对象, 也可以是纯虚构对象. 这取决于你要处理的系统操作有多少:

  • 如果系统操作相对来说比较少, 并且职责基本都是相关的, 那么可以让代表整个”系统”、”根对象”、”设备”或”子系统”的领域对象来充当控制器, 简单的来说, 就相当于”系统”的外观;
  • 否则, 虚构一个代表用例的控制器是首要选择, 通常被称作用例或会话控制器 (以下统一称为用例控制器), Handler 或者 Session 是对它的有效命名方法. -- 这也是六边形构架推荐的方式

用例控制器维护与同一个用例相关的工作流, 如对数据流进行封装、转换, 还可以维护关于用例状态的信息 (例如 Session), 如果你的系统有完善的异常处理机制, 通常异常也应从底层向上抛出到控制器中处理;

注意要避免控制器的职责过多, 把本应是领域层对象的职责也给分配给控制器是不对的, 这会形成臃肿控制器 (这种现象在 MVC 及其它三层模型变体中很常见, 因为它们是贫血模型, 那么必然会造成 Controller 层的臃肿 ).

在设计控制器时, 通常会采用 GOF 中的外观模式, 例如下图中的 DB.java, 该类为 java.sql 包中复杂且全面的逻辑提供了一个非常简单的、特定于处理 Product 用例的外观.