在上一篇中,我们讲述了CQRS和Event Sourcing的相关概念以及他们能解决什么问题。尽管可以在不适用任何其他框架或库的情况下实现CQRS/ES,但我们还是建议使用已有的一些工具。这些工具可以简化开发过程,同时运行开发人员专注于业务逻辑的处理,避免重复的造轮子。在本节中,我们将选择Axon框架来实现CQRS/ES。
Axon是一个轻量级的Java开源框架,可以帮助构建你构建基于CQRS模式的可伸缩、可扩展和库维护的Java应用程序。它还可以帮助你准备Event Sourcing所需要的环境。Axon提供了所有重要构建模块的实现,如聚合、存储库,命令和时间总线。Axon可以让开发人员的工作更为轻松。
Axon让我们避免了负载的配置和对数据流的操作,我们可以专注于应用程序业务规则的定制,而不是创建样板代码。使用Axon可以获得如下的一些优势:
在默认情况下,Axon以经提供了对Spring Boot的集成支持。我们只需要通过一些简单的配置步骤就可以将Axon与Spring Boot整合在一起。
第一步是使用合适的项目构建工具在项目中配置Axon依赖项。以下是使用Gradle来配置Axon的方法:
dependencies{
compile("org.axonframework:axon-spring-boot-starter:3.2")
compile("org.axonframework:axon-mongo:3.2")
testCompile("org.axonframework:axon-test:32.")
}
第一个依赖项为我们提供了与Spring Boot集成的最基本的Axon所有必要组件,如命令中线,事件总线和聚合。第二个依赖项是为我们的聚合或事件配置库提供所需的基本环境。最后一个依赖项用于构建先关的测试环境。
根据需要配置Axon所需要的一些Spring Bean。比如EventHandlerConfiguration(负责控制事件处理程序行为的组件),如果其中有一个事件执行失败,则终止处理后续的所有事件。当然这是非必须的,但是还是值得在应用程序中进行此配置,以防止系统中数据的不一致。配置代码如下:
@Configuration
public class AxonConfig {
private final EventHandlingConfiguration eventHandlingConfiguration;
@Autowired
public AxonConfig(EventHandlingConfiguration eventHandlingConfiguration) {
this.eventHandlingConfiguration = eventHandlingConfiguration;
}
@PostConstruct
public void registerErrorHandling() {
eventHandlingConfiguration.configureListenerInvocationErrorHandler(configuration -> (exception, event, listener) -> {
String msg = String.format(
"[EventHandling] Event handler failed when processing event with id %s. Aborting all further event handlers.",
event.getIdentifier());
log.error(msg, exception);
throw exception;
});
}}
这里的主要思想是创建一个额外的配置文件(使用@Configuration注释的类)。该类的构造函数注入由Spring自身所管理的EventHandlingConfiguration依赖项。由于绑定依赖,我们可以在此对象上调用configureListenerInvocationErrorHandler()并通过记录异常将异常传播到上层去处理错误。
我们使用MongoDB来存储Event Store中所发生的所有事件。要实现此功能,可以通过如下的方法来实现:
@Bean
public EventStorageEngine eventStore(MongoTemplate mongoTemplate) {
return new MongoEventStorageEngine(
new JacksonSerializer(), null, mongoTemplate, new DocumentPerEventStorageStrategy());
}
这样,在事件总线上发布的所有事件都将自动保存到MongoDB数据库中。通过这种简单的配置,我们就可以在应用程序中使用MongoDB的数据源。
在Axon配置方面就是这样的简单。当然,我们还有其他很多的配置方式。但我们可以通过上述简单的配置,就可以使用Axon的功能了。
根据上图,创建命令,将命令传递给命令总线然后创建事件并将事件放在事件总线上还不是CQRS。我们必须记住改变写入存储库的状态并从读取数据库中读取当前状态,这才是CQRS模式的关键点。
配置此流程也并不复杂。在将命令传递给命令网关时,Spring将名利类型作为参数以搜索带有@CommandHandler 注释的方法。
@Value
class SubmitApplicationCommand {
private String appId;
private String category;
}
@AllArgsConstructor
public class ApplicationService {
private final CommandGateway commandGateway;
public CompletableFuture<Void> createForm(String appId) {
return CompletableFuture.supplyAsync(() -> new SubmitExpertsFormCommand(appId, "Android"))
.thenCompose(commandGateway::send);
}
}
除其他事项外,命令处理程序负责将创建的事件发送到事件总线。它将事件对象放置到AggregateLifecycle静态导入的apply()方法中。然后调度该事件以查找预期的处理程序,并且由于我们配置了事件存储库,所有事件都自动保存在数据库中。
@Value
class ApplicationSubmittedEvent {
private String appId;
private String category;
}
@Aggregate
@NoArgsConstructor
public class ApplicationAggregate {
@AggregateIdentifier
private String id;
@CommandHandler
public ApplicationAggregate(SubmitApplicationCommand command) {
//some validation
this.id = command.getAppId;
apply(new ApplicationSubmittedEvent(command.getAppId(), command.getCategory()));
}
}
要更改写库的状态,我们需要提供一个使用@EventHandler注释的方法。该应用程序可以包含多个事件处理程序。他们每个人都应该执行一个特定的任务,如发送电子邮件,记录或保存在数据库中。
@RequiredArgsConstructor
@Order(1)
public class ProjectingEventHandler {
private final IApplicationSubmittedProjection projection;
@EventHandler
public CompletableFuture<Void> onApplicationSubmitted(ExpertsFormSubmittedEvent event) {
return projection.submitApplication(event.getApplicationId(), event.getCategory());
}
如果我们想确定所有事件处理程序的处理顺序,我们可以用@Order注释一个类并设置一个序列号。submitApplication()方法负责进行所有必要的更改并将新的数据存储到写库中。
这些都是使我们的应用程序采用CQRS模式原则的关键点。当然,这些原则只能应用于我们应用程序的某些部分,具体取决于业务需求。Event Sourcing不适合我们正在构建的每个应用程序或模块。在实现此模式时也要谨慎,因为更复杂的应用程序可能难以维护。
使用Axon框架,CQRS和Event Sourcing的实现变得简单化。有关高级配置的更多详细信息,请访问Axon的网站https://docs.axonframework.org/。
from https://zhuanlan.zhihu.com/p/63167853