AxonFramework,EventStore实现

事件溯源存储库需要一个事件存储(event store)从聚合中存储和加载事件。事件存储提供了一个事件总线的功能,增加了持久化已发布的事件,并能够基于聚合标识符检索事件。

Axon提供了一个开箱即用的事件存储,EmbeddedEventStore。它委托事件实际的存储和检索到EventStorageEngine。
有多个EventStorageEngine的实现:

JpaEventStorageEngine

JpaEventStorageEngine存储事件在JPA-compatible数据源中。JPA事件存储存储事件在所谓的条目中。这些条目包含事件的序列化形式,以及存储一些领域元数据以快速查找这些条目。使用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. 在这个示例中,事件存储有一个特定的持久化单元。然而,你可能会选择将第三行添加到任何其他持久化单元的配置中。
  2. 本行注册DomainEventEntry(由JpaEventStore使用的类)到持久化上下文。

注意
Axon使用锁来防止两个线程访问相同的聚合。然而,如果在同一数据库上你有多个jvm,这不会帮助你。在这种情况下,你不得不依靠数据库来检测冲突。并发访问事件存储将导致违反主键约束(Key Constraint Violation),因为表允许聚合只能有一个任何序列号的事件,所以,用已有的序列号为现有聚合插入第二个事件将导致错误。
JpaEventStorageEngine可以检测这个错误并把它转换成ConcurrencyException。然而,每个数据库系统以不同的方式报告此违规行为。如果你用JpaEventStore注册你的数据源,它将尝试检测数据库的类型,并找出错误代码是一个违反主键约束(Key Constraint Violation)。或者,你可能会提供一个PersistenceExceptionTranslator实例,如果一个给定的异常代表一个违反主键约束(Key Constraint Violation)它能分辨。
如果没有提供数据源或PersistenceExceptionTranslator,从数据库驱动程序按原样抛出异常。

默认情况下,JPA事件存储引擎需要一个EntityManagerProvider实现,返回EventStorageEngine使用的EntityManager实例。这还允许应用程序管理所用的持久化上下文。EntityManagerProvider的责任是提供一个正确的EntityManager实例。

有几个EntityManagerProvider的实现可用,各有不同的需求。SimpleEntityManagerProvider仅在构建时返回EntityManager实例给它。这使得实现成为容器管理上下文一个简单的选择。ContainerManagedEntityManagerProvider作为一种选择,返回默认的持久化上下文,并且它的使用默认通过JPA事件存储。
如果你有一个持久化单元称为“myPersistenceUnit”,你希望在JpaEventStore中使用,这就是EntityManagerProvider实现,可能看起来像:

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实体中。虽然在许多情况下这就足够了,你可能会遇到这些实体提供的元数据不够的情况。或者你可能想将不同的聚合类型的事件存储在不同的表。
如果是这样,你可以扩展JpaEventStorageEngine。它包含了一些protected方法,你可以重写来调整其行为。

警告
注意持久化提供者,如Hibernate,在它们的EntityManager实现上使用一级缓存。通常,这意味着在查询中使用或返回的所有隶属于EntityManager的实体。它们只有在周围事务被提交或在事务中执行显式“清除”时才被清除。当查询在事务上下文中执行时尤其如此。
要解决这个问题,请确保仅仅查询非实体对象。你可以使用JPA的“SELECT new SomeClass(parameters) FROM ...”风格的查询来解决这个问题。或者,获取一批事件后调用EntityManager.flush()和EntityManager.clear()。未能这样做当加截大事件流时可能导致OutOfMemoryExceptions。

JDBC Event Storage Engine

JDBC事件存储引擎使用JDBC连接将事件存储在JDBC兼容的数据存储。通常,这些都是关系数据库。从理论上讲,任何一个JDBC驱动程序都可以用来支持JDBC事件存储引擎。

类似于JPA,JDBC事件存储引擎将事件存储在条目中。默认情况下,每个事件存储在一个单独的条目中,对应表中的一行。一个表用于事件,另一个用于快照。

JdbcEventStorageEngine使用ConnectionProvider来获取连接。通常,这些连接可以直接从数据源中获得。然而,Axon会将这些连接绑定到一个工作单元,以便在一个工作单元使用一个连接。这将确保一个单独的事务用于存储所有事件,即使在同一线程中嵌套多个工作单元。

注意
Spring用户建议使用SpringDataSourceConnectionProvider从数据源附加连接到现有的事务。

MongoDB Event Storage Engine

MongoDB是一个基于文档的NoSQL存储。其可伸缩特性使它适合用于事件存储。Axon提供了MongoEventStorageEngine,使用MongoDB作为数据库支持。它包含在Axon Mongo模块(Maven artifactId axon-mongo)。

事件存储在两个独立的集合中:一个用于实际的事件流,一个用于快照。

默认情况下,MongoEventStorageEngine将存储各自的事件在各自的文档。然而,它是可能改变StorageStrategy使用。

Axon提供的选择是DocumentPerCommitStorageStrategy,为在一个单独的提交中存储所有事件,创建一个单独的文档(即在同一DomainEventStream)。

在一个单独的文档中存储整个提交的好处在于提交是原子存储的。此外,它只需要对任意数量的事件进行一次往返。缺点是,它变得更加难以直接在数据库中查询事件。例如,当重构领域模型时,如果他们被包含在“commit document”中,很难从一个聚合“transfer”事件到另一个聚合。

MongoDB不需要很多配置。它所需要的只是对一个存储事件集合的引用,然后你就可以开始了。在生产环境中,你可能想要对集合中的索引进行双重检查。

Event Store Utilities

Axon提供了一些事件存储引擎,可能在某些情况下是有用的。

SequenceEventStorageEngine是围绕其它两个事件存储引擎的包装器。当读取时,它从这两个事件存储引擎中返回事件。附加事件只是附加到第二个事件存储引擎。出于性能原因使用事件存储两种不同的实现的情况下,这是有用的,例如。第一个是一个更大的,但较慢事件存储,而第二个是优化的快速读取和写入。

还有一个常驻内存的存储事件EventStorageEngine实现:InMemoryEventStorageEngine。虽然它可能优于任何其他的事件存储,这并不意味着长期生产使用。然而,它在需要事件存储的short-lived工具或测试中非常有用,。

Influencing the serialization process(影响序列化过程)

事件存储需要为存储准备一种序列化事件的方法。默认情况下,Axon使用XStreamSerializer,其使用XStream序列化成XML事件。XStream是相当快的,比Java序列化更灵活。此外,XStream序列化的结果是人类可读的。对日志和调试而言非常有用。

XStreamSerializer可以配置。你可以定义它应该用于某些包、类甚至字段的别名。除了可以缩短潜在的长名称之外,还可以在事件的类定义更改时使用别名。有关别名的更多信息,访问XStream网站。

另外,Axon还提供了JacksonSerializer,使用Jackson将事件序列化为JSON。当它生成一个更紧凑的序列化形式,它要求类遵守Jackson所要求的约定(或配置)。

注意
使用Java代码(或其他JVM语言)配置序列化器是很容易的。然而,由于它调用方法的局限性,在Spring XML程序上下文看配置它并不那么简单。其中一个选项是创建一个FactoryBean,创建一个XStreamSerializer实例,并配置它的代码。检查 Spring 参考更多的信息。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 226,488评论 6 524
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 97,466评论 3 411
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 174,084评论 0 371
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 62,024评论 1 305
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 70,882评论 6 405
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 54,395评论 1 318
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 42,539评论 3 433
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 41,670评论 0 282
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 48,194评论 1 329
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 40,173评论 3 352
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 42,302评论 1 362
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,872评论 5 354
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 43,581评论 3 342
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,984评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 35,179评论 1 278
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,888评论 3 385
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 47,306评论 2 369

推荐阅读更多精彩内容