For a while we are working on the Axon framework. One of the main components of Axon is the event store. Axon framework has a JPA based event store as well as a file based event store. Within the incubator we have also been working on the MongoDB based event store. In this blog post I am going to explain the internals of the MongoDB event store. I will also discuss some of the next steps we want to take with the mongo event store.


Incubator Status

The mongo event store is becoming fully functional right now. At the moment the event store is functional for the 1.3.x branch of axon. We have a working example “The Axon Trader” using the event store. We do not have it running in a project in production yet. If you are considering to do use it in production please let us know about your findings.

The planning is to drop the incubator status starting Axon 2.0. Using the mentioned sample we are going to do some stress testing and we are thinking about improvements. More on these in the Next steps section.

Connecting to Mongo

MongoFactory

Mongo can be used as a single instance, which is not really suitable for a production environment. A better setup is to use a replication set of mongo instances. Using the MongoFactory this can be hidden for the other parts of the application.

The factory takes a list of server addresses, you can override the MongoOptions as well as the WriteConcern. Of course we take good defaults. There is an option to set the mode to single instance or multiple instance using a system property: axon.mongo.singleinstance. This system property takes precedence over other settings.

In the end, the MongoFactory is used to create a Mongo instance. This instance should be provided to the implementation of the MongoCollections class. More on this later on.

MongoOptionsFactory

The configuration of Mongo using the java driver is executed using the MongoOptions class. This factory is used to create such an object using defaults but with the possibility to provider other values for the most important properties. Some of the options you can configure are: number of connections per host, the connection time-out and auto-connect retry.

The collections in Mongo

Mongo uses collections to store data. The names of these collections that Axon uses for the event store as well as the saga store can be configured. Of course logical default values are provided as well. If you want to use other collections you can provide your own implementation of this interface or provide other names to the default implementation of the interface.

Overview of the classes

The following image gives you an overview of the important classes.

Axon mongo eventstore

Event repository

The event repository, MongoEventStore, is responsible for storing and retrieving the events. Just like the other implementations of the event stores, you can provide your own event serializer. By default XStream is used to serialize the events.

The java driver for mongo makes it easy to store new documents in Mongo and query the existing documents. By using the EventStoreCollections object we obtain a reference to the actual collection and by calling insert we add new documents to the collection. To get an idea about the code, have a look at the following function of the event store.

public void appendEvents(String type, DomainEventStream events) {
    List<DBObject> entries = new ArrayList<DBObject>();
    while (events.hasNext()) {
        DomainEvent event = events.next();
        EventEntry entry = new EventEntry(type, event, eventSerializer);
        entries.add(entry.asDBObject());
    }
    eventStoreCollections.domainEventCollection().insert(entries.toArray(new DBObject[entries.size()]));
}

A similar method is available to store snapshot events.

It becomes interesting when reading events to recreate an aggregate. Before reading events that need to be applied, we first need to find the latest snapshot. The following code block gives an idea about finding the latest snapshot event.

private EventEntry loadLastSnapshotEvent(String type, AggregateIdentifier identifier) {
    DBObject mongoEntry = BasicDBObjectBuilder.start()
            .add(EventEntry.AGGREGATE_IDENTIFIER_PROPERTY, identifier.asString())
            .add(EventEntry.AGGREGATE_TYPE_PROPERTY, type)
            .get();
    DBCursor dbCursor = eventStoreCollections.snapshotEventCollection()
            .find(mongoEntry)
            .sort(new BasicDBObject(EventEntry.SEQUENCE_NUMBER_PROPERTY, -1))
            .limit(1);

    if (!dbCursor.hasNext()) {
        return null;
    }
    DBObject first = dbCursor.next();

    return new EventEntry(first);
}

Now you have seen the main features of the event store in Mongo. The next step is to store and load Saga instances.

Saga repository

The mongo part for Saga’s is even easier than the events. We do not have to account for snapshots, which makes it easier. Just use the identifier of a saga instance to store, load and delete a document. The repository supports storing the associations between Saga’s as required by Axon. It also supports the ResourceInjector if your saga needs other resources to do it’s job. Think about the command bus to send new commands.

The structure is the same as for the event store implementation. The SagaStoreCollections provides access to the collections in the mongo database as well as the database itself.

Next steps

We are constantly looking for improvements in the mongo implementation. Allard is working on a specific Mongo serializer. That way we are nog serializing to XML and storing that xml. We will store the content in a DBObject and that way in a basic mongo document structure. More on this will follow later on in another blog post.

As mentioned we are planning on doing some performance tests and experiment with indexes to improve performance.

I you have improvements, bugs or ideas feel free to file an issue on our Github project.

Tagged on: