1. 六边形架构
六边形架构又称为端口-适配器,这个名字更容器理解。六边形架构将系统分为内部(内部六边形)和外部,内部代表了应用的业务逻辑,外部代表应用的驱动逻辑、基础设施或其他应用。内部通过端口和外部系统通信,端口代表了一定协议,以API呈现。一个端口可能对应多个外部系统,不同的外部系统需要使用不同的适配器,适配器负责对协议进行转换。这样就使得应用程序能够以一致的方式被用户、程序、自动化测试、批处理脚本所驱动,并且,可以在与实际运行的设备和数据库相隔离的情况下开发和测试。
六边形架构的核心理念是:应用是通过端口与外部进行交互的
。
在下图的六边形架构中,红圈内的核心业务逻辑(应用程序和领域模型)与外部资源(包括 APP、Web 应用以及数据库资源等)完全隔离,仅通过适配器进行交互。它解决了业务逻辑与用户界面的代码交错问题,很好地实现了前后端分离。六边形架构各层的依赖关系是由外向内依赖的。
六边形架构将系统分为内六边形和外六边形两层,这两层的职能划分如下:
- 红圈内的六边形实现应用的核心业务逻辑;
- 外六边形完成外部应用、驱动和基础资源等的交互和访问,对前端应用以 API 主动适配的方式提供服务,对基础资源以依赖倒置被动适配的方式实现资源访问。
六边形架构的一个端口可能对应多个外部系统,不同的外部系统也可能会使用不同的适配器,由适配器负责协议转换。这就使得应用程序能够以一致的方式被用户、程序、自动化测试和批处理脚本使用。
2. CQRS
2.1 什么是CQRS
CQRS,作为一种战术办法,是实现DDD建模领域的最佳途径之一。事实上,它就是因为这个目标而诞生在这个世界上。
CQRS本身只是一个读写分离
的架构思想,全称是:Command Query Responsibility Segregation,即命令查询职责分离
,表示在架构层面,将一个系统分为写入
(命令)和查询
两部分。一个命令表示一种意图,表示命令系统做什么修改,命令的执行结果通常不需要返回;一个查询表示向系统查询数据并返回。
CQRS架构中,另外一个重要的概念就是事件
,事件表示命令操作领域中的聚合根,然后聚合根的状态发生变化后产生的事件。
2.2 采用CQRS架构的一个前提
由于CQRS架构的一致性模型为最终一致性,所以,你的系统要接受查询到的数据可能不是最新的,而是有几个毫秒的延迟。之所以会有这个前提,是因为CQRS架构考虑到,作为一个多用户同时访问的互联网应用,当在高并发修改数据的情况下,比如秒杀、12306购票等场景,用户UI上看到的数据总是旧的。比如你秒杀时提交订单前看到库存还大于0,但是当你提交订单时,系统提示你宝贝卖完了。这个就说明,在这种高并发修改同一资源的情况下,任何人看到的数据总是Stale的,即旧的。
2.3 实现方式
CQRS作为一种架构思想,可以有多种实现方式:
- 最常见的CQRS架构是数据库的读写分离;
- 系统底层存储不分离,但是上层逻辑代码分离;
- 系统底层存储分离,C 端采用 Event Sourcing 的技术,在 EventStore 中存储事件;Q 端存储对象的最新状态,用于提供查询支持;
Event Sourcing - 事件溯源:
- 不保存对象的最新状态,而是保存对象产生的所有事件;
- 通过事件溯源(Event Sourcing, ES)得到对象最新状态。
2.4 CQRS的适用场景
- 当我们的应用的写模型和读模型差别比较大时;
- 当我们希望实践DDD时;因为CQRS架构可以让我们实现领域模型不受任何ORM框架带来的对象和数据库的阻抗失衡的影响;
- 当我们希望对系统的查询性能和写入性能分开进行优化时,尤其是读/写比非常高的系统,CQ分离是必须的;
- 当我们希望我们的系统同时满足高并发的写、高并发的读的时候;因为CQRS架构可以做到C端最大化的写,Q端非常方便的提供可扩展的读模型。
这里我主要分享的CQRS架构是上面第3种实现方式,也就是上图所画的架构。在我心目中,只有第三种才是真正意义上的CQRS架构。
2.5 CQRS架构的数据流
C 端的命令的执行流程
客户端如(MVC Controller)发送命令通知系统做修改:
- 发送命令到分布式MQ;
- 然后命令的订阅者处理命令;
- 订阅者内部根据不同的命令调用不同的 Command Handler 进行处理;
- Command Handler 内部根据命令所指定的聚合根 ID 从 In-Memory 内存中直接获取聚合根对象的引用,然后操作聚合根对象;
- 聚合根对象状态发生变化并产生事件;
- 框架负责自动持久化事件到 Event Storage(简称EventStore);
- 框架负责将事件发布到 Event MQ;
- Event 订阅者订阅事件,然后调用对应的 Event Handler 进行处理,如更新 Data Storage(保存了聚合根的最新状态,通常叫读库,ReadDB)。
Q端的查询的执行流程
客户端如(MVC Controller)发出查询请求系统返回数据:
- 调用轻薄的 Query Service,传如 Query DTO;
- Query Service 从读库进行查询并返回结果。
读库可以有很多种,依据我们的业务场景来选择:比如关系型DB、分布式缓存等NoSQL、搜索引擎,etc。
2.6 CQRS\ES - 架构演进