倉儲和事件存儲
倉儲主要用于操作聚合。倉儲通常在我們持久化數(shù)據(jù)的時候充當了一個網關角色。在CQRS中,倉儲只做了根據(jù)聚合的ID來查詢出對應的聚合。而其他一些類型的查詢應該走查詢數(shù)據(jù)庫(query databases).
在Axon框架中,所有的倉儲都必須實現(xiàn)Repository接口。這個接口定義了三個方法:load(identifier,version), load(identifier) and newInstance(factoryMethod). 這個load方法請請允許你從倉儲中加載聚合。而version參數(shù)通常用于檢查當前數(shù)據(jù)是否被更新過(請參閱高級沖突檢測和解決方案)。newInstance則用于向倉儲注冊新的聚合實例。
根據(jù)你的基礎持久存儲需求,這里有很多基礎實現(xiàn),這些實現(xiàn)提供了大多數(shù)存儲庫所需的基本功能。Axon框架在保存聚合的當前狀態(tài)的倉儲(請參閱標準倉儲)和存儲聚合事件的倉儲兩者上做出了一些區(qū)分(請參閱事件溯源倉儲)。
注意,Repository接口沒有刪除(標識符)方法。他是通過調 用聚合內的AggregateLifecycle.markDeleted()方法來刪除的。聚合刪除和其他情況一樣,他們是一種狀態(tài)改變,唯一的區(qū)別是在很多情況下它是不可逆的。你應該在你的聚合上創(chuàng)建你自己認為有意義的方法,以此來將聚合的狀態(tài)設置為’已刪除’狀態(tài)。你可以注冊你想要發(fā)布的任何事件。
標準倉儲
標準倉儲實際存儲的是聚合的當前狀態(tài)。每次更新后,新的狀態(tài)都會覆蓋舊的狀態(tài)。這使得應用程序的查詢組件和命令組件可以使用相同的信息。根據(jù)你創(chuàng)建的應用程序的類型,這可能是最簡單的方案。如果是這樣的話,Axon提了一些模塊來幫助你實現(xiàn)這樣一個倉儲。
Axon框架提供了一些開箱即用的標準倉儲實現(xiàn):GenericJpaRepository。這個聚合被期望是一個有效的JPA實體。他通過配置一個EntityManagerProvider提供EntityManager來管理實際的持久化,以及指定在倉儲中的實際聚合類型的類。當聚合調用靜態(tài)方法AggregateLifecycle.apply()時,你也可以通過發(fā)布到EventBus的事件來充當。
你也可以很容易實現(xiàn)你自己的倉儲。那樣的話,最好的方式就是繼承于LockingRepository這個類。而聚合的包裝類型,建議使用AnnotatedAggregate。 具體請查看GenericJpaRepository的源代碼。
事件源倉儲
聚合根可以根據(jù)配置的事件源倉儲來加載事件從而重新構建它自己的狀態(tài)。這些倉儲不會存儲聚合本身,他們存儲的是聚合產生的一系列事件。基于這些事件,你可以在任何時間來重新生成聚合的狀態(tài)。
EventSourcingRepository提供了Axon框架中任何事件源倉儲所須的基本功能。但是他依賴于EventStore(請參閱事件存儲實現(xiàn)),它抽象出事件的實際存儲機制。
當然,你也可以提供一個Aggregate工廠。AggregateFactory指定如何創(chuàng)建一個聚合實例。 一旦創(chuàng)建了聚合,EventSourcingRepository會用從事件倉儲加載的事件來進行初始化它。 Axon框架附帶了一些您可能會用到的AggregateFactory實現(xiàn)。 如果他們不夠用,創(chuàng)建你自己的實現(xiàn)是非常容易的。
GenericAggregateFactory
GenericAggregateFactory是一個特殊的AggregateFactory實現(xiàn),可用于任何類型的事件源聚合根。 GenericAggregateFactory會創(chuàng)建一個倉儲管理的Aggregate類型的實例。 Aggregate類必須是非抽象類,并聲明一個沒有初始化的默認的無參數(shù)構造函數(shù)。
GenericAggregateFactory適用于大多數(shù)場景,其中聚合不需要特別注入不可序列化的資源。
SpringPrototypeAggregateFactory
取決于你的架構,使用spring將依賴關系注入到聚合中可能會有一些幫助。比如,您可以將查詢倉儲注入到您的聚合中,以確保某些值的存在(或不存在)。
為了將依賴關系注入到你的聚合中,你須要在定義了SpringPrototypeAggregateFactory的spring上下文中配置一個聚合根的原型bean. 它不是使用構造函數(shù)創(chuàng)建實例,而是使用Spring應用程序上下文來實例化聚合而且他會將其他依賴注入到你的聚合根中。
實現(xiàn)你自己的AggregateFactory
在有些情況下,GenericAggregateFactory并不能滿足你的須求。比如,你有一個抽象聚合,他有多個實現(xiàn)(例如,PublicUserAccount和BackOfficeAccount都是繼承于Account這個聚合)。你應該使用單個倉儲而不是為每個聚合創(chuàng)建一個倉儲,并且配置一個能知道不同實現(xiàn)的AggregateFactory。
Aggregate Factory所做的大部分工作是創(chuàng)建未初始化的Aggregate實例。 它必須使用給定的聚合標識符和流中的第一個事件。通常來說,這個事件是一個創(chuàng)建事件,它包含關于預期的聚合類型的提示。你可以使用這些信息來選擇他具體的哪一個實現(xiàn)類并調用其構造方法。確保該方法是沒有使用任何事件,并且這個聚合必須是已經被初始化了。
同簡單的存儲庫直接加載聚合的實現(xiàn)相比,基于事件初始化聚合可能是一項耗時的工作。而CachingEventSourcingRepository提供了一個緩存,可以從中加載聚合(如果可用)。
Event store 的實現(xiàn)
Event Sourcing倉儲需要一個事件存儲(event store)來存儲和加載來自聚合的事件。 事件存儲提供了事件總線的功能,除此之外,它還可以持久化已發(fā)布的事件,并且能夠基于聚合標識符來檢索事件。
Axon提供了一個開箱即用的事件存儲,即EmbeddedEventStore。 它將事件的實際存儲和檢索委托給EventStorageEngine來管理。
這里有多個EventStorageEngine實現(xiàn)可用:
JpaEventStorageEngine
JpaEventStorageEngine將事件存儲在兼容JPA的數(shù)據(jù)源中。 JPA Event Store存儲的事件被稱為實體。這些實體不僅包括了事件的序列化形式,還提供了為快速查找到這些實體而存儲的一些元數(shù)據(jù)字段。要使用JpaEventStorageEngine,您必須在您的類路徑上具有JPA(javax.persistence)注解。
默認情況下,事件存儲須要配置持久化上下文(比如,在META-INF/persistence.xml文件中定義),這個上下文主要包括DomainEventEntry和SnapshotEventEntry類(他們都在org.axonframework.eventsourcing.eventstore.jpa包中)
下面是一個配置持久化上下文的例子:
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
<persistence-unit name="eventStore" transaction-type="RESOURCE_LOCAL"> (1)
<class>org...eventstore.jpa.DomainEventEntry</class> (2)
<class>org...eventstore.jpa.SnapshotEventEntry</class>
</persistence-unit>
</persistence>
1.在這個例子中,事件存儲有一個特定的持久化單元。但是,您可以選擇將第三行添加到任何其他持久化單元配置中。
- 該行將DomainEventEntry(由JpaEventStore使用的類)注冊到持久性上下文中。
注意:
Axon使用鎖定來防止兩個線程同時訪問相同的聚合。 但是,如果您在同一個數(shù)據(jù)庫上有多個JVM,這不會對您有所幫助。 在這種情況下,您必須依靠數(shù)據(jù)庫來檢測沖突。 對事件存儲的并發(fā)訪問將導致主鍵唯一性沖突,因為該表記錄的是單個事件,而每個事件有自己的序列號。 如果數(shù)據(jù)庫中已有一個帶了序列號的聚合事件,再將第二個相同的序列號(唯一標識)聚合事件寫入時就會失敗。
JpaEventStorageEngine可以檢測到此錯誤并將其轉換為ConcurrencyException。 但是,每個數(shù)據(jù)庫系統(tǒng)都會以不同的方式報告此沖突。 如果您使用JpaEventStore來注冊的DataSource,它將嘗試檢測數(shù)據(jù)庫的類型并找出哪些錯誤代碼表示主鍵唯一性沖突。 或者,您可以提供一個PersistenceExceptionTranslator實例, 該實例可以知道給定的異常表示主鍵唯一性沖突。
如果沒有提供數(shù)據(jù)源或PersistenceExceptionTranslator,數(shù)據(jù)庫驅動程序會把異常按原樣拋出。
默認情況下,JPA事件存儲引擎需要一個EntityManagerProvider實現(xiàn),這個實現(xiàn)會返回供EventStorageEngine使用的EntityManager實例。 這也允許使用應用程序管理的持久性上下文。 EntityManagerProvider的責任是提供一個正確的EntityManager實例。當然也可以使用應用程序管理持久化上下文。EntityManagerProvider的責任是提供一個正確的EntityManager實例。
EntityManagerProvider有一些實現(xiàn)可直接使用,每個實現(xiàn)都可以滿足不同的需求。SimpleEntityManagerProvider只會在實例化時才返回給它EntityManager實例。 這使得實現(xiàn)對于容器管理的上下文來說是一個簡單的選項。 另外,還有一個ContainerManagedEntityManagerProvider,它返回默認的持久化上下文,并由Jpa事件存儲默認使用。
如果您有一個名為“my PersistenceUnit”的持久性單元,您希望在JpaEventStore中使用它,那么這就是EntityManager Provider實現(xiàn)的例子:
public class MyEntityManagerProvider implements EntityManagerProvider {
?
private EntityManager entityManager;
?
@Override
public EntityManager getEntityManager() {
return entityManager;
}
?
@PersistenceContext(unitName = "myPersistenceUnit")
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
默認情況下。JPA事件存儲實體是在DomainEventEntry和SnapshotEventEntry中。雖然這在大多數(shù)情況下就足夠了,但您可能會遇到這些實體提供的元數(shù)據(jù)不足的情況。 或者您可能想要將不同聚合類型的事件存儲在不同的表中。
在這種情況下,你可以繼承JpaEventStorageEngine該類。它包含一些protected的方法,您可以重寫以實現(xiàn)不用的行為。
警告:
注意,持久化提供者(如Hibernate)在EntityManager實現(xiàn)上使用了一級緩存。通常,這意味著在查詢中使用或返回的所有實體都與EntityManager相連。它們只在事務被提交時被清除,或者在事務中執(zhí)行明確的“清除”。尤其是當查詢在事務上下文中執(zhí)行時。
要解決這個問題,請確保只對非實體對象進行查詢。您可以使用JPA的"SELECT new SomeClass(parameters) FROM..."風格查詢來解決這個問題?;蛘撸@取一批事件后調用entitymanager.flush()和entitymanager.clear()。當加載一個非常大的事件流時可能會導致OutOfMemoryException。
JDBC 事件存儲引擎
JDBC事件存儲引擎使用JDBC連接將事件存儲在兼容JDBC的數(shù)據(jù)庫中。 通常,這些是關系數(shù)據(jù)庫。 理論上,任何具有JDBC驅動程序的東西都可以用來支持JDBC事件存儲引擎。
與JPA相似,JDBC事件存儲引擎將條目(entries)中的事件存儲起來。 默認情況下,每個事件都存儲在單個條目中,該條目與表中的一行相對應。 一個表用于存事件,另一個用于存快照。
JdbcEventStorageEngine使用ConnectionProvider獲取連接。 通常,這些連接可以直接從DataSource獲得。 但是,Axon會將這些連接綁定到工作單元,以便在工作單元中使用單個連接。 這確保了即使多個工作單元嵌套在同一個線程中,也可以使用單個事務來存儲所有事件。
注意:
建議Spring用戶使用SpringDataSourceConnectionProvider從DataSource連接到現(xiàn)有的事務。
MongoDB事件存儲引擎
MongoDB是一個基于文檔的NoSQL存儲。 其可擴展性使其適合用作Event Store。 Axon提供了MongoEventStorageEngine,它使用MongoDB作為后臺數(shù)據(jù)庫。 它包含在Axon Mongo模塊(Maven artifactId axon-mongo)中。
事件存儲在兩個獨立的集合(collections)中:一個用于實際事件流,另一個用于快照。
默認情況下,MongoEventStorageEngine將每個事件存儲在單獨的文檔中。 但是,可以通過過StorageStrategy來修改。 Axon提供的替代方案是DocumentPerCommitStorageStrategy,它為已經存儲在單個提交中(即在同一個DomainEventStream中)的所有事件創(chuàng)建單個文檔。
將整個提交在單個文檔中的優(yōu)點是:該提交以原子方式保存的。 此外,對于任何數(shù)量的事件,它只需要一次往返。 缺點是直接在數(shù)據(jù)庫中查詢事件變得更加困難。 例如,當重構領域模型時,如果將事件包含在“提交文檔”中,則將事件從一個集合“轉移”到另一個集合是比較困難的。
MongoDB不需要很多配置。 它所需要的只是對一個存儲事件集合的引用,然后您就可以開始了。對于生產環(huán)境,您可能需要對集合上的索引進行雙重檢查。
Event Store Utilities
Axon提供了許多在某些情況下可能有用的事件存儲引擎。
將多個事件存儲到一起
SequenceEventStorageEngine是兩個事件存儲引擎的包裝。在查詢的時候,它會從兩個事件存儲引擎中返回事件。新增的事件會交給第二個事件存儲引擎。例如,在出于性能原因使用兩種不同的事件存儲實現(xiàn)的情況下,這個效果是非常的明顯。第一個是比較大但查詢很慢的事件存儲庫,第二個為快速讀取和寫入而優(yōu)化。
過濾存儲事件
FilteringEventStorageEngine允許事件根據(jù)謂詞(predicate)進行過濾。 只有符合此謂詞的事件才會被存儲。 請注意,使用事件存儲作為事件源的事件處理程序可能無法接收這些事件,因為它們未被存儲。
內存存儲引擎
這里有一個EventStorageEngine的實現(xiàn)。它將存儲的事件保存在內存中,他就是InMemoryEventStorageEngine類。雖然他可能優(yōu)于其他任何的事件存儲,但是他基本不用于生產環(huán)境中。但是,它對于需要將事件存儲的短期(short-lived)工具或測試來說就非常有用。
Influencing the serialization process
事件存儲需要用一種序列化方式把事件序列化后存儲起來。 默認情況下,Axon使用XStreamSerializer,它使用XStream將事件序列化為XML。 XStream速度相當快,且比Java序列化更靈活。 此外,XStream序列化的結果是易讀的。 對于日志記錄和調試目的非常有用。
XstreamSerializer是可以配置的。 您可以定義它應該用于具體的包,類或甚至字段的別名。 除了縮短可能的長名稱之外,還可以在事件的類定義發(fā)生更改時使用別名。 有關別名的更多信息,請訪問XStream網站。
另外,Axon還提供了JacksonSerializer,它使用Jackson將事件序列化為JSON。 雖然它生成更緊湊的序列化表單,但它需要類遵循Jackson所要求的約定(或配置)。
您也可以實現(xiàn)自己的序列化類,只需創(chuàng)建一個實現(xiàn)Serializer類,并配置事件存儲庫以使用該實現(xiàn)類來替代默認值。
Serializing Events vs 'the rest'
從Axon 3.1版本開始,可以使用不同的序列化器來存儲事件, 而Axon的有些對象變不須要序列化器(例如命令,快照,Sagas等)。 雖然XStreamSerializer幾乎可以序列化任何對象,但是它的輸出并不會百分百的和其他應用程序融合。 JacksonSerializer有更好的輸出,但需要在序列化對象時指定結構。 這種結構通常存在于事件中,所以他非常合適的事件序列化器。
通過配置API,您可以簡單地注冊一個事件串行器如下:
Configurer configurer = ... // 初始化
configurer.configureEventSerializer(conf -> /* create serializer here*/);
如果未配置顯式的eventSerializer,則使用已配置的主序列化程序對事件進行序列化(該序列化程序默認為XStream序列化程序)。
Event Upcasting(事件向上轉型)
由于軟件應用程序性質的不斷變化,事件定義很可能隨著時間而改變。 由于事件存儲被視為只讀和新增(不會有更新和刪除操作,更新和刪除操作都是新增事件和刪除事件來做)數(shù)據(jù)源,因此應用程序必須能夠讀取所有事件,而不管它們何時添加。 這是事件向上轉型的用處。
最初這個是來源于面向對象編程的概念,其中“子類在需要時自動轉換為其超類”,向上轉型(upcasting)的概念也可以應用于事件源。 Upcast一個事件意味著將其從原來的結構轉變?yōu)樾碌慕Y構。 與OOP的轉換(upcasting)不同,事件轉換(upcasting)不能完全自動完成,因為新事件的結構對于舊事件是未知的。 必須提供手工的Upcasters來指定如何將舊結構轉換(upcasting)為新結構。
Upcasters是這樣的一個類,它接受修訂版為x的一個輸入事件,并輸出零個或多個修訂版為x + 1的新事件。此外,upcasters是在一個處理鏈中處理,這意味著一個upcaster的輸出被作為鏈中下一個upcaster的輸入。 這允許您以增量方式更新事件,為每個新事件修訂編寫一個Upcaster,使它們變得小巧,獨立并且易于理解。
注意:
也許Upcasting的最大好處是它允許你做非破壞性的重構,即完整的歷史事件保持不變(不會被修改)。
在本節(jié)中,我們將教你如何編寫Upcasters,隨著Axon不同的Upcaster實現(xiàn),并解釋事件的序列化形式如何影響upcasters的編寫。
為了允許upcaster查看他們正在接收的序列化對象的版本,事件存儲將存儲一個version以及事件的完全限定名稱。這個version是由在序列化器中配置的RevisionResolver生成的。Axon提供了幾個RevisionResolver的實現(xiàn),比如AnnotationRevisionResolver,它檢查在事件有效的payload的@Revision注解,SerialVersionUIDRevisionResolver 使用Java Serialization API和FixedValueRevisionResolver所定義的serialVersionUID,它總是返回一個預定義的值。后者在注入當前應用程序版本時是有用的。這將允許你看哪個版本的應用程序生成一個特定的事件。
Maven用戶可以使用MavenArtifactRevisionResolver自動使用項目版本。它使用項目的groupId和artifactId進行初始化以獲取版本。由于這只適用于由Maven創(chuàng)建的JAR文件,所以版本不能總是由IDE解決。如果某個版本無法解析,則返回null。
Maven用戶可以使用MavenArtifactRevisionResolver,它會自動使用項目版本。它使用項目的groupId和artifactId進行初始化以獲取版本。由于這只適用于由Maven創(chuàng)建的JAR文件,所以版本不能總是由IDE解決。如果某個版本無法被解析,則返回null。
Axon的upcasters不直接使用EventMessage,而是使用IntermediateEventRepresentation。 IntermediateEventRepresentation提供了檢索所有必要字段的功能,以構建一個EventMessage(所以它也是一個upcasters的EventMessage)以及實際的upcast函數(shù)。這些upcasters功能在默認情況下只允許調整事件payload,payload類型以及添加事件的元數(shù)據(jù)。 upcast函數(shù)中事件的實際表示可能因使用的事件序列化器或期望的表單類型而異,所以IntermediateEventRepresentation的upcast函數(shù)允許選擇期望的表示類型。其他字段,例如消息/聚合標識符,聚集類型,時間戳等不能通過IntermediateEventRepresentation進行調整。調整這些字段不是Upcaster的預期工作,因此這些選項不是由提供的IntermediateEventRepresentation實現(xiàn)提供的。
Axon框架中事件的基礎Upcaster接口在IntermediateEventRepresentations流上工作,并返回一個IntermissionEventRepresentations的流。 因此,upcasting并不直接返回引入的upcast函數(shù)的最終結果,而是通過IntermediateEventRepresentations的處理鏈將每個upcast函數(shù)從一個修訂鏈接傳輸?shù)搅硪粋€。一旦這個過程發(fā)生,最終的結果被從它們中取出來,也就是說在序列化的事件上執(zhí)行了實際的upcasting函數(shù)。
如何寫一個upcaster
以下Java代碼片段將作為一對一Upcaster(SingleEventUpcaster)的基本示例。
事件的舊版本:
@Revision("1.0")
public class ComplaintEvent {
private String id;
private String companyName;
?
// Constructor, getter, setter...
}
新版本的事件:
@Revision("2.0")
public class ComplaintEvent {
private String id;
private String companyName;
private String description; // New field
?
// Constructor, getter, setter...
}
Upcaster:
// Upcaster from 1.0 revision to 2.0 revision
public class ComplaintEventUpcaster extends SingleEventUpcaster {
private static SimpleSerializedType targetType = new SimpleSerializedType(ComplainEvent.class.getTypeName(), "1.0");
?
@Override
protected boolean canUpcast(IntermediateEventRepresentation intermediateRepresentation) {
return intermediateRepresentation.getType().equals(targetType);
}
?
@Override
protected IntermediateEventRepresentation doUpcast(IntermediateEventRepresentation intermediateRepresentation) {
return intermediateRepresentation.upcastPayload(
new SimpleSerializedType(targetType.getName(), "2.0"),
org.dom4j.Document.class,
document -> {
document.getRootElement()
.addElement("description")
.setText("no complaint description"); // Default value
return document;
}
);
}
}
Spring boot配置:
@Configuration
public class AxonConfiguration {
?
@Bean
public SingleEventUpcaster myUpcaster() {
return new ComplaintEventUpcaster();
}
?
@Bean
public JpaEventStorageEngine eventStorageEngine(Serializer serializer,
DataSource dataSource,
SingleEventUpcaster myUpcaster,
EntityManagerProvider entityManagerProvider,
TransactionManager transactionManager) throws SQLException {
return new JpaEventStorageEngine(serializer,
myUpcaster::upcast,
dataSource,
entityManagerProvider,
transactionManager);
}
}
Content type conversion
升級器(upcaster)在給定的內容類型(例如dom4j Document)上工作。 為了提高upcasters之間的額外靈活性,處理鏈中的各upcasters之間的內容類型可能會有所不同。 Axon將嘗試使用ContentTypeConverters自動在內容類型之間進行轉換。 它將搜索從類型x到類型y的最短路徑,執(zhí)行轉換并將轉換后的值傳遞到請求的升級器。 出于性能原因,只有在接收升級器上的canUpcast方法為真時才會執(zhí)行轉換。
ContentTypeConverters可能取決于使用的序列化程序的類型。 試圖將bytes[]轉換為dom4j Document除非使用將事件寫為XML的序列化程序,否則將無濟于事。 為確保UpcasterChain可以訪問特定于序列化程序的ContentTypeConverters,可以將對序列化程序的引用傳遞給UpcasterChain的構造函數(shù)。
建議:
要獲得最佳性能,請確保同一鏈中的所有升級者(其輸出是另一個輸入者)使用相同的內容類型。
如果您所需要的內容類型轉換不是由Axon提供的,您可以使用ContentTypeConverter接口編寫自己的內容。
XStreamSerializer支持Dom4J以及XOM作為XML文檔。JacksonSerializer支持Jackson的JsonNode。
快照
當聚合會存活很長時間時,并且它們的狀態(tài)不斷變化時,它們會產生大量事件。我們不得不加載所有這些事件來重建聚合的狀態(tài)可能會對性能產生巨大的影響??煺帐录蔷哂刑厥庥猛镜挠蚴录核鼘⑷我鈹?shù)量的事件匯總為一個事件。通過定期創(chuàng)建和存儲快照事件,事件存儲不必返回長期事件列表而只是返回最后一個快照事件和快照創(chuàng)建后發(fā)生的所有事件。
例如,庫存物品往往會經常變化。每次出售物品時,事件會將庫存減少一個。每次裝了運新貨物時,庫存都會增加一些數(shù)量。如果您每天銷售一百件商品,那么您每天至少會產生100個事件。幾天后,您的系統(tǒng)將花費太多時間去獲取所有這些事件,以確定是否應該提出“ItemOutOfStockEvent”。而當一個快照事件替代很多這些事件,只需通過存儲當前的庫存數(shù)量即可。
創(chuàng)建一個快照
快照的創(chuàng)建可以由多種因素來觸發(fā),例如自上次快照創(chuàng)建的事件數(shù)量,初始化聚合的時間超過某個閾值,基于時間等。目前,Axon提供了一種機制,允許您根據(jù)事件計數(shù)閾值來觸發(fā)創(chuàng)建快照。
SnapshotTriggerDefinition接口定義了何時應該創(chuàng)建快照。
EventCountSnapshotTriggerDefinition提供了在加載聚合需要的事件數(shù)量超過特定閾值時觸發(fā)快照創(chuàng)建的機制。如果加載聚合所需的事件數(shù)量超過了配置的閾值,觸發(fā)器會通知Snapshotter為聚合創(chuàng)建快照。
快照觸發(fā)器配置在事件溯源倉儲中,并具有許多可選配置屬性:
Snapshotter:設置實際的快照程序實例,負責創(chuàng)建和存儲實際的快照事件;
Trigger: 設置觸發(fā)快照創(chuàng)建的閾值;
Snapshotter負責實際創(chuàng)建快照。通常,快照是一個應盡可能的和業(yè)務處理程序分開。因此,建議在不同的線程中運行快照。 Snapshotter接口聲明了一個方法:scheduleSnapshot(),它將聚合的類型和唯一標識符作為參數(shù)。
Axon提供了一個AggregateSnapshotter,它可以創(chuàng)建和存儲AggregateSnapshot實例。 這是一種特殊類型的快照,因為它包含了具體的實際聚合實例。 Axon提供的倉儲知道這種類型的快照,并將從中提取聚合,而不是實例化一個新聚合。 在快照事件之后加載的所有事件都將流式傳輸?shù)教崛〉木酆蠈嵗?/p>
注意:
您必須保證使用的Serializer實例(默認為XStreamSerializer)能夠序列化您的聚合。 XStreamSerializer要求您使用Hotspot JVM,否則您的聚合必須具有可訪問的默認構造函數(shù)或實現(xiàn)了Serializable接口。
AbstractSnapshotter提供了一組基本的屬性,使您可以修改創(chuàng)建快照的方式:
EventStore主要用于加載過去事件并存儲快照的事件存儲。此事件存儲必須實現(xiàn)SnapshotEventStore接口。
Executor設置執(zhí)行程序,例如ThreadPoolExecutor,它將提供線程來處理實際的快照創(chuàng)建。默認情況下,快照是在調用scheduleSnapshot()方法的線程中創(chuàng)建的,通常不推薦用于生產。
AggregateSnapshotter提供了另外的屬性:
AggregateFactories是這樣一個屬性,它用于配置創(chuàng)建聚合實例的工廠。配置多個聚合工廠允許您使用單個Snapshotter為各種聚合類型創(chuàng)建快照。 EventSourcingRepository實現(xiàn)提供對他們使用的AggregateFactory的訪問。這可用于在Snapshotter中配置與倉庫中使用的相同的聚合工廠。
注意:
如果您在另一個線程中執(zhí)行創(chuàng)建快照,請確保根據(jù)需要為基礎事件存儲配置正確的事務管理。
Spring用戶可以使用SpringAggregateSnapshotter,當需要創(chuàng)建快照時,SpringAggregateSnapshotter將自動從應用程序上下文中查找正確的AggregateFactory。
?