enode是基于JVM平台,为业务实践Domain Driven Design思想而落地的一个应用框架,使用CQRS, Event Sourcing设计模式,让开发者可以专注于业务模型建模和业务逻辑开发
Reactiveenode是完全响应式的框架,是实现高吞吐的核心设计db层面使用了异步驱动,实现全链路异步- 针对
IO密集型操作,集成了kotlin coroutine
Event Sourcing- 聚合根的事件完全持久化,记录聚合根的状态变化,让
C端的数据可追溯,数据持久化变得通用化
- 聚合根的事件完全持久化,记录聚合根的状态变化,让
Event Driven- 业务流程以事件驱动为核心,让研发更聚焦业务中领域事件的建设和积累
- 先进的
Saga机制,以事件驱动的流程管理器(Process Manager)的方式支持一个用户操作跨多个聚合根的业务场景,如订单处理,从而避免分布式事务的使用
CQRS- 基于
CQRS架构思想,enode解决了CQRS架构的C端的高并发写的问题,以及CQ两端数据同步的顺序性保证和幂等性 - 支持
Fire And Forget和Fire And Wait两种方式返回命令执行结果
- 基于
Fast and Flexible- 聚合根常驻内存(
In-Memory Domain Model),在设计上尽可能的避免了聚合根重建,可以完全以OO的方式来设计实现聚合根,不必为ORM的阻抗失衡而烦恼 - 聚合根的处理基于
Actor思想,乐观并发控制,无锁,在事件持久化层面使用Group Commit Domain Event提高写性能
- 聚合根常驻内存(
enode在架构层面严格规范了研发人员该如何写代码,要求用DDD的方式思考,严格遵守聚合内强一致性、聚合之间最终一致性的原则
在设计上遵循SOLID,以下均可扩展替换成自建
- 针对
IoC容器,目前SpringBoot友好适配 - 针对
CommandBus,只要求最基础的队列能力,目前适配了Kafka、RocketMQ、Pulsar、AMQP - 针对
EventStore,适配了MySQL、PostgreSQL、MongoDB - 针对
ReplyService,要求实现点对点通信模型,实现Command处理结果的通知
- 一个命令一次只能修改一个聚合根
- 聚合间只能通过领域消息交互
- 聚合内强一致性
- 聚合间最终一致性
- 编排(
Choreography) 参与者(子事务)之间的调用、分配、决策和排序,通过交换事件进行进行。是一种去中心化的模式,参与者之间通过消息机制进行沟通,通过监听器的方式监听其他参与者发出的消息,从而执行后续的逻辑处理。
enode中使用的就是这种模式
- 控制(
Orchestration) 提供一个控制类,方便参与者之间的协调工作。事务执行的命令从控制类发起,按照逻辑顺序请求Saga的参与者,从参与者那里接受到反馈以后,控制类在发起向其他参与者的调用。所有Saga的参与者都围绕这个控制类进行沟通和协调工作。
Apache ServiceComb使用的是这种模式
新增了三个注解,系统限定了只扫描@Command和@Event标识的类,执行的方法上需要添加@Subscribe注解:
@Command@Event@Subscribe
启动时会扫描包路径下的注解,注册成Spring Bean,和@Component作用相同。
定义了docker-compose.yml,搭配docker-compose快速的在启动一套运行环境,用于应用的测试
docker-compose up -d-
目前enode函数调用的实现是放在
kotlin coroutine中来执行的,这里涉及到实际执行的任务类型,针对计算密集型和IO密集型的任务,目前没有做可定制化的配置,后续的版本会考虑加上, 使用也很简单,@Subscribe方法体加上suspend标记即可。 -
针对
Java异步编程做了深度优化,支持CommandHandler和EventHandler中定义CompletableFuture返回值,阻塞调用封装在协程中,避免使用#join() #get()等阻塞代码,同时也支持kotlin suspend
可参考samples模块中的例子
转账的例子中,转账的业务场景,涉及了三个聚合根:
- 银行存款交易记录,表示一笔银行存款交易
- 银行转账交易记录,表示一笔银行内账户之间的转账交易
- 银行账户聚合根,封装银行账户余额变动的数据一致性
CQRS架构中的Command端应用
主要用来接收来自用户的Command请求,核心是将Command发送到消息队列。
- 消费
Command队列中的消息的服务
将领域事件消息持久化才算是
Command执行成功,Command执行的结果可以通过发送命令时注册的监听器获取。
- 领域事件处理服务
事件可能会多次投递,所以需要消费端逻辑保证幂等处理,这里框架无法完成支持,需要开发者自己实现。
