DDD 领域驱动设计与六边形架构
软件世界是对现实世界的抽象, 而传统的三层架构或 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 用例的外观.