在上一篇中,咱們講述了CQRS和Event Sourcing的相關概念以及他們能解決什麼問題。儘管能夠在不適用任何其餘框架或庫的狀況下實現CQRS/ES,但咱們仍是建議使用已有的一些工具。這些工具能夠簡化開發過程,同時運行開發人員專一於業務邏輯的處理,避免重複的造輪子。在本節中,咱們將選擇Axon框架來實現CQRS/ES。html
Axon是一個輕量級的Java開源框架,能夠幫助構建你構建基於CQRS模式的可伸縮、可擴展和庫維護的Java應用程序。它還能夠幫助你準備Event Sourcing所須要的環境。Axon提供了全部重要構建模塊的實現,如聚合、存儲庫,命令和時間總線。Axon可讓開發人員的工做更爲輕鬆。java
Axon讓咱們避免了負載的配置和對數據流的操做,咱們能夠專一於應用程序業務規則的定製,而不是建立樣板代碼。使用Axon能夠得到以下的一些優點:spring
在默認狀況下,Axon以經提供了對Spring Boot的集成支持。咱們只須要經過一些簡單的配置步驟就能夠將Axon與Spring Boot整合在一塊兒。數據庫
第一步是使用合適的項目構建工具在項目中配置Axon依賴項。如下是使用Gradle來配置Axon的方法:markdown
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全部必要組件,如命令中線,事件總線和聚合。第二個依賴項是爲咱們的聚合或事件配置庫提供所需的基本環境。最後一個依賴項用於構建先關的測試環境。app
根據須要配置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中所發生的全部事件。要實現此功能,能夠經過以下的方法來實現:spring-boot
@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的網站docs.axonframework.org/。
原文做者:ŁukaszKucik
原文地址:www.nexocode.com/blog/posts/…
譯 者:譚朝紅