Code migration

The API of Axon 2 is different than Axon 1. We think it’s a lot better. Unfortunately, this means that migration to Axon 2 is not as easy as updating dependencies.

An easy way to migrate is by changing dependencies and chasing compile errors in your IDE. Once you code compiles and your tests pass, you’re well on your way. Below is a checklist you can use to make sure you didn’t forget anything, and that provides information about how different compile errors should be solved.

Should you have any questions about the process, you can always post a message on the mailinglist.

1 Update project dependencies

Make sure to use the same version for axon-test or any other modules that you have included. You don’t want to mix-and-match different versions.

2 Change event hierarchy

Events no longer need to extend from one of the base classes, such as DomainEvent or ApplicationEvent. Wherever you events extend those classes (they will surely generate a compile error in your IDE), remove te extends … part.

It is likely that there is no field that contains the identifier of the aggregate that raised the event. If there is, you’re find. If there isn’t, add such a field. It can be of any type. String is a good defult, but explicit identifier type (such as OrderId) are always better.

3 Identity and Aggregate roots

Now it’s time to visit your aggregate roots. They should still extend from the same class, but they don’t have to pass the aggregate identifier to the super class anymore.

If you use the AbstractAnnotatedAggregateRoot, you should create a field that stores the aggregate identifier, and annotate it with @AggregateIdentifier. If you extend another class, you will need to implement the abstract getIdentifier() method. Aggregates have a generic type identifier that you can use to make the identifier type you use explicit: AbstractAnnotatedAggregateRoot<String>

The aggregate identifier in Axon 2 may be of any type, as long as that type has a consistent toString() method. Axon uses the toString() method to convert the aggregate identifier to a String when it is stored in the Event Store (or as an association in a Saga).

Instead of a parameterized constructor, such as AbstractAnnotatedAggregateRoot(AggregateIdentifier), the GenericAggregateFactory uses a no-arg constructor (similar to most other libraries).

In the Event Handler of the Event that creates the aggregate, make sure the Aggregate Identifier field is assigned a value.

4 Fixtures (tests)

In Axon 2, fixtures explicitly expose the type of aggregate they test:

private FixtureConfiguration fixture;

and:

fixture = Fixtures.newGivenWhenThenFixture(MyAggregate.class)

Instead of registering a repository, the fixture generates one that you can use:

commandHandler.setMyAggregateRepository(fixture.getRepository);

If you use a custom repository type that you would like to include in the test, you can use registerRepository() on the fixture.
If your aggregate contains @CommandHandler annotated Command Handlers, you do not need to explicitly configure a command handler on the fixture. It will have automatically detected that during initialization of the fixture.

5 Event Handlers

Since Events no longer extend from one of the abstract Event classes, such as DomainEvent or ApplicationEvent, you cannot get the Timestamp or Event ID from those objects. In Axon 2, this information is kept in the EventMessage that wraps the Events.

In Event Handler where the timestamp, MetaData or Event ID is needed, you may add an extra parameter of type EventMessage. This extra parameter can be used to extract the information. Alternatively, you may add a parameter annotated with @MetaData(key) to extract a specific meta data value from the event message. The parameter of the method must match (or be a superclass of) the type of value in the meta data. A third option is to add a parameter of type (joda) DateTime, annotated with @Timestamp. This will inject the Event’s timestamp into the parameter.

6 Command Dispatching

While in Axon 1 you could pass any object to the send method on a Command Bus, you now need to pass a CommandMessage. The easiest way to wrap an object into a command message is by using the static GenericCommandMessage.asCommandMessage() method.

However, you should consider using a Command Gateway instead. You can use a DefaultCommandGateway, or create your own by defining an interface. Check out Section 3.1, The Command Gateway in the reference guide.

7 Spring configuration

The SpringTransactionalInterceptor is no longer needed in Axon 2. Instead, you should define the transaction manager directly on the Command Bus implementation:

As asynchronous event listeners are deprecated, the executors attribute on the <axon:annotation-config> element must be removed. It serves no purpose anymore.

Event Store migration

If you have used Axon 1 with Event Sourcing in production, you have probably generated an Event Store with a number of Events. The format of the Event Store in Axon 2 is slightly different than that of Axon 1. As a result, you need to migrate these events.

Migrating can be done while the application is running. There is no impact on the running application. Of course, the processing uses CPU, so the application may run a bit slower while you keep the database busy.

If you use another type of Event Store, you may need to convert the Event Store yourself. The souce code of the Migration Tool will provide some code that you should be able to re-use to make it easier. If you need help, you can always get in touch.