同步操作将从 anruence/enode 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
ENode是一个基于【DDD】【CQRS】【ES】【EDA】【In-Memory】架构风格的应用框架
sendAsync只关注发送消息的结果 executeAsync发送消息的同时,关注命令的返回结果,返回的时机如下:
event的订阅者可能有很多个,所以enode只要求有一个订阅者处理完事件后发送结果给发送命令的人即可,通过AbstractDomainEventListener中sendEventHandledMessage参数来设置是否发送,最终来决定由哪个订阅者来发送命令处理结果
ICommandHandler是为了操作内存中的聚合根的,所以不会有异步操作,但后来ICommandHandler的Handle方法也设计为了HandleAsync了,目的是为了异步到底,否则异步链路中断的话,异步就没效果了 而ICommandAsyncHandler是为了让开发者调用外部系统的接口的,也就是访问外部IO,所以用了Async ICommandHandler,ICommandAsyncHandler这两个接口是用于不同的业务场景,ICommandHandler.handleAsync方法执行完成后,框架要从context中获取当前修改的聚合根的领域事件,然后去提交。而ICommandAsyncHandler.handleAsync方法执行完成后,不会有这个逻辑,而是看一下handleAsync方法执行的异步消息结果是什么,也就是IApplicationMessage。
聚合根需要定义一个无参构造函数,因为聚合根初始化时使用了
aggregateRootType.getDeclaredConstructor().newInstance();
新增了三个注解,系统限定了只扫描@Command和@Event标识的类,执行的方法上需要添加@Subscribe注解
发送命令代码
CompletableFuture<AsyncTaskResult<CommandResult>> future = commandService.executeAsync(createNoteCommand, CommandReturnType.EventHandled);
消费命令消息
@Command
public class CreateNoteCommandHandler {
/**
* Handle the given aggregate command.
*/
@Subscribe
public void handleAsync(ICommandContext context, CreateNoteCommand command) {
Note note = new Note(command.getAggregateRootId(), command.getTitle());
context.add(note);
}
}
领域事件消费
@Event
public class NoteEventHandler {
@Subscribe
public AsyncTaskResult handleAsync(NoteTitleChanged evnt) {
System.out.println(String.format("Note denormalizered, title:%s, Version: %d", evnt.getTitle(), evnt.version()));
return AsyncTaskResult.Success;
}
@Subscribe
public AsyncTaskResult handleAsync(NoteTitleChanged2 evnt) {
System.out.println(String.format("Note denormalizered, title:%s, Version: %d", evnt.getTitle(), evnt.version()));
return AsyncTaskResult.Success;
}
}
@Bean(initMethod = "start", destroyMethod = "shutdown")
public CommandResultProcessor commandResultProcessor() {
CommandResultProcessor processor = new CommandResultProcessor(6000);
return processor;
}
@Bean(initMethod = "init")
public ENodeBootstrap eNodeBootstrap() {
ENodeBootstrap bootstrap = new ENodeBootstrap();
bootstrap.setPackages(Lists.newArrayList("com.enodeframework.samples"));
return bootstrap;
}
需要下面两张表来存储事件
CREATE TABLE `EventStream`
(
`Sequence` BIGINT AUTO_INCREMENT NOT NULL,
`AggregateRootTypeName` VARCHAR(256) NOT NULL,
`AggregateRootId` VARCHAR(36) NOT NULL,
`Version` INT NOT NULL,
`CommandId` VARCHAR(36) NOT NULL,
`CreatedOn` DATETIME NOT NULL,
`Events` MEDIUMTEXT NOT NULL,
PRIMARY KEY (`Sequence`),
UNIQUE KEY `IX_EventStream_AggId_Version` (`AggregateRootId`, `Version`),
UNIQUE KEY `IX_EventStream_AggId_CommandId` (`AggregateRootId`, `CommandId`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
CREATE TABLE `PublishedVersion`
(
`Sequence` BIGINT AUTO_INCREMENT NOT NULL,
`ProcessorName` VARCHAR(128) NOT NULL,
`AggregateRootTypeName` VARCHAR(256) NOT NULL,
`AggregateRootId` VARCHAR(36) NOT NULL,
`Version` INT NOT NULL,
`CreatedOn` DATETIME NOT NULL,
PRIMARY KEY (`Sequence`),
UNIQUE KEY `IX_PublishedVersion_AggId_Version` (`ProcessorName`, `AggregateRootId`, `Version`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
多选一
https://kafka.apache.org/quickstart
bin/zookeeper-server-start.sh config/zookeeper.properties
bin/kafka-server-start.sh config/server.properties
https://rocketmq.apache.org/docs/quick-start/ 启动RocketMQ服务
nohup sh bin/mqnamesrv &
nohup sh bin/mqbroker -n 127.0.0.1:9876 &
CQRS架构中的Command端应用 主要用来接收Command,将Command发送到消息队列
消费Command队列中的消息的服务 将领域事件消息持久化才算是Command执行成功,Command执行的结果可以通过发送命令时注册的监听器获取
领域事件处理服务 事件可能会多次投递,所以需要消费端逻辑保证幂等处理
http://localhost:8001/note/create?id=noteid&t=notetitle&c=commandid
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。