This guide takes you through the steps needed to set up a simple Axon 2 based architecture in 7 simple steps. But before you get started, you might want to create new project in your IDE. In this guide, you’ll need the axon-core and axon-test modules, as well as their dependencies. Everything you need is in the Axon 2 Full Package.

  1. Creating the domain model
  2. Implementing the Commands and Events
  3. Creating the test scenarios
  4. Implementing an Aggregate
  5. Setting up the infrastructure
  6. Adding an Event Listener
  7. Wrapping up…
  8. Migrating from Axon 1.x?

1 Domain Modeling

Unlike many other frameworks, Axon is not built to “get you started quickly”. It’s built to keep you going for a long time. But since you’re probably eager to find out how Axon works out for you, we’ll keep this one short.

Before starting to code your first Aggregate, you’ll need to identify what the actual aggregate is. This is generally not a one-shot bulls eye story. It’s trying, failing, trying, failing, succeeding, finding out you failed again, … well, you get the point. Domain Modeling is an iterative process, where new insights need to be constantly used to refactor the model.

In this quickstart, we’re keeping it short. The domain modeling, failure, succeeding, etc. has been done for you, and the result is that we need only a single aggregate: The ToDoItem. And this ToDoItem allows us to keep track of things we need, uh, to do.

To Do Item
  • CreateToDoItemCommand
    • ToDoIItemCreatedEvent
  • MarkCompletedCommand
    • ToDoItemCompletedEvent
 

Note how you can use a UML-like definition of your aggregate. The first level items are the commands you can execute on a ToDoItem. The second level items are the Events that are caused by these Commands. All terms used here must be understood (or better, defined) by the product owner. If they’re not, you’re most likely making a mistake.

A tip when doing domain modeling with a product owner: when modeling aggregates, define in terms of what you can do with them. Not in terms of the data they store. Once you catch the behavior of a domain concepts, it’s much easier to find the data you need. If you have only the data, you’re still nowhere.

Since this is an mission critical application, that requires heavy auditing capabilities and extreme performance, we decide to use Event Sourcing. A real no-brainer in this case…

2 Implement Commands and Events

In step 1, we have defined 2 Commands and 2 Events. Now, we’ll create an implementation for them. They’re pretty straightforward. Unlike in the modeling process, now, we’re interested in the data involved.

Make sure Events (and preferably also your Commands) are immutable. When possible, use final fields.

First, the code for the Command and Event to create an item:

public class CreateToDoItemCommand {

    @TargetAggregateIdentifier
    private final String todoId;
    private final String description;

    public CreateToDoItemCommand(String todoId, String description) {
        this.todoId = todoId;
        this.description = description;
    }

    public String getTodoId() {
        return todoId;
    }

    public String getDescription() {
        return description;
    }
}
public class ToDoItemCreatedEvent {

    private final String todoId;
    private final String description;

    public ToDoItemCreatedEvent(String todoId, String description) {
        this.todoId = todoId;
        this.description = description;
    }

    public String getTodoId() {
        return todoId;
    }

    public String getDescription() {
        return description;
    }
}

Note that the @TargetAggregateIdentifier annotation is not always necessary. When using a distributed command bus, or the disruptor command bus, this annotation marks the field that identifies the aggregate to be invoked. Axon uses that information to route commands to the correct destination.

Code duplication? Yes, in this case the Command and Event are almost identical. In practice, they tend to diverge more, as the models are in general more complex than this one.
If you really hate duplication so much that the explicitness isn’t even worth it, rest assured. In Axon 2 it is possible to use a single object as both the Command and the Event. But you’re warned! One day, you’ll find out that they’re actually not the same…  Therefore, we’ll stick to separate classes in this guide.

And the Command and Event to mark an item as completed:

public class MarkCompletedCommand {

    @TargetAggregateIdentifier
    private final String todoId;

    public MarkCompletedCommand(String todoId) {
        this.todoId = todoId;
    }

    public String getTodoId() {
        return todoId;
    }
}

Note the @TargetAggregateIdentifier annotation on the todoId field. Axon needs this annotation to find the correct aggregate instance the Command targets. We’ll dive deeper into this one on step 4 and step 5.

public class ToDoItemCompletedEvent {

    private final String todoId;

    public ToDoItemCompletedEvent(String todoId) {
        this.todoId = todoId;
    }

    public String getTodoId() {
        return todoId;
    }
}

3 Create test scenarios

One of the big advantages of using explicit Command and Event objects, is that they can be used to describe the Aggregate’s API using functional terms. As an effect, it is possible to define the test scenarios using these same terms. Since it’s all functional, you can refactor at will, with limited risk of needing to change the tests.

Let’s create such a scenario. And yes, we don’t even have an implementation yet!

First, we need to create a Test Fixture for our aggregate-to-create. I’ll be using JUnit as test framework, but you can choose any framework you like.

public class ToDoItemTest {

    private FixtureConfiguration fixture;

    @Before
    public void setUp() throws Exception {
        fixture = Fixtures.newGivenWhenThenFixture(ToDoItem.class);
    }
}

After fixing all the imports, one error remains: ToDoItem cannot be found. That makes sense, as we didn’t create that class yet. I’m sure your IDE has a shortcut/quickfix to create the class on the fly. Make sure it extends AbstractAnnotatedAggregateRoot, which we will further discuss in step 4.

Your aggregate will look as follows:

public class ToDoItem extends AbstractAnnotatedAggregateRoot {
}

The test fixture will automatically set up a basic infrastructure to execute the tests. For now, this infrastructure suits our needs. This means we can start writing the scenarios.

    @Test
    public void testCreateToDoItem() throws Exception {
        fixture.given()
               .when(new CreateToDoItemCommand("todo1", "need to implement the aggregate"))
               .expectEvents(new ToDoItemCreatedEvent("todo1", "need to implement the aggregate"));
    }

    @Test
    public void testMarkToDoItemAsCompleted() throws Exception {
        fixture.given(new ToDoItemCreatedEvent("todo1", "need to implement the aggregate"))
               .when(new MarkCompletedCommand("todo1"))
               .expectEvents(new ToDoItemCompletedEvent("todo1"));
    }

Now comes the fun part: running the tests. So, press the “start” button in you IDE and….. hmm… fail. Of course it does. We didn’t implement the aggregate yet.

4 Implement the Aggregate

When running the test, we get a NoHandlerForCommandException: No handler was subscribed to command [your command here...]. That makes sense, because there is no aggregate that can deal with that command. Let’s implement the ToDoItem, step by step.

First, we create a Command Handler for the CreateToDoItem. Since we want this Command to create a new instance of a ToDoItem, we create a Constructor, which we annotated with @CommandHandler. The apply() method tells Axon that we want to apply this Event to the aggregate, and have it stored, and published to other components.

public class ToDoItem extends AbstractAnnotatedAggregateRoot {

    @CommandHandler
    public ToDoItem(CreateToDoItemCommand command) {
        apply(new ToDoItemCreatedEvent(command.getTodoId(), command.getDescription()));
    }
}

When we run the tests again, we get another exception: IncompatibleAggregateException: The aggregate [ToDoItem] doesn’t provide a no-arg constructor.
Easy, let’s create a no-arg constructor then. Axon uses the no-arg constructor by default to create uninitialized aggregate instances. Check out the reference guide for ways to change that.

When you run the tests again, more bad news. The tests fails “normally”, stating that no Events have been published. Beneath the table, there is a possible explanation: IncompatibleAggregateException: The aggregate class [ToDoItem] does not specify an Identifier

We need to annotate the field containing the Aggregate identifier with @AggregateIdentifier. That fields doesn’t exist yet, so we’ll create it. We’ll also create an Event Handler that sets the value of the identifier when the Aggregate is created.

This is what the aggregate looks like now:

public class ToDoItem extends AbstractAnnotatedAggregateRoot {

    @AggregateIdentifier
    private String id;

    public ToDoItem() {
    }

    @CommandHandler
    public ToDoItem(CreateToDoItemCommand command) {
        apply(new ToDoItemCreatedEvent(command.getTodoId(), command.getDescription()));
    }

    @EventHandler
    public void on(ToDoItemCreatedEvent event) {
        this.id = event.getTodoId();
    }
}

Now, run the test again… you should see a green light now! Hurrah. Well, only one of the tests passes, the other fails with a familiar exception. Let’s go fix it.

This time, we want to add a Command Handler that acts on an existing aggregate. Remember we annotated the Commands with @TargetAggregateIdentifier? That tells Axon which Aggregate instance to load. So all that remains is to implement the method (not a constructor, this time).

    @CommandHandler
    public void markCompleted(MarkCompletedCommand command) {
        apply(new ToDoItemCompletedEvent(id));
    }

All tests should go green now! Hurrah!

5 Set up the infrastructure

Although some developers are happy when tests pass, the product owner generally doesn’t really care. He want to have a UI and great performance. So let’s build one.

UI’s take a lot of time to build, and have nothing to do really with Axon. So our UI is a public static void main method…. exciting!

Let’s sum up the building blocks that we need in our infrastructure:

  • Command Bus and a Command Gateway
  • Event Sourcing Repository and Event Store
  • Event Bus

We can either create and wire these building blocks programmatically, or using Spring. Pick your side…

Programmatically - The pure Java way
public class ToDoItemRunner {

    public static void main(String[] args) {
        // let's start with the Command Bus
        CommandBus commandBus = new SimpleCommandBus();

        // the CommandGateway provides a friendlier API
        CommandGateway commandGateway = new DefaultCommandGateway(commandBus);

        // we'll store Events on the FileSystem, in the "events/" folder
        EventStore eventStore = new FileSystemEventStore(new SimpleEventFileResolver(new File("./events")));

        // a Simple Event Bus will do
        EventBus eventBus = new SimpleEventBus();

        // we need to configure the repository
        EventSourcingRepository repository = new EventSourcingRepository(ToDoItem.class);
        repository.setEventStore(eventStore);
        repository.setEventBus(eventBus);

        // Axon needs to know that our ToDoItem Aggregate can handle commands
        AggregateAnnotationCommandHandler.subscribe(ToDoItem.class, repository, commandBus);

        // and let's send some Commands on the CommandBus.
        final String itemId = UUID.randomUUID().toString();
        commandGateway.send(new CreateToDoItemCommand(itemId, "Need to do this"));
        commandGateway.send(new MarkCompletedCommand(itemId));
    }
}
Spring - Using an XML Application Context

Before you start, you need to set up the correct dependency for Spring.
For maven:

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version></version>
    </dependency>

We’ll need a Spring configuration file, as well as a Runner to bootstrap the context. Here is the context file:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:axon="http://www.axonframework.org/schema/core"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.axonframework.org/schema/core http://www.axonframework.org/schema/axon-core-2.0.xsd">

    <axon:command-bus id="commandBus"/>
    <axon:event-bus id="eventBus"/>

    <axon:event-sourcing-repository id="toDoRepository"
                                    aggregate-type="org.axonframework.test.sample.ToDoItem"/>

    <axon:aggregate-command-handler id="toDoItemHandler"
                                    aggregate-type="org.axonframework.test.sample.ToDoItem"
                                    repository="toDoRepository"
                                    command-bus="commandBus"/>

    <axon:filesystem-event-store id="eventStore" base-dir="events"/>

    <bean class="org.axonframework.commandhandling.gateway.CommandGatewayFactoryBean">
        <property name="commandBus" ref="commandBus"/>
    </bean>

</beans>

And here is the bootstrap file:

public class ToDoItemRunner {

    private CommandGateway commandGateway;

    public ToDoItemRunner(CommandGateway commandGateway) {
        this.commandGateway = commandGateway;
    }

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("sampleContext.xml");
        ToDoItemRunner runner = new ToDoItemRunner(applicationContext.getBean(CommandGateway.class));
        runner.run();
    }

    private void run() {
        final String itemId = UUID.randomUUID().toString();
        commandGateway.send(new CreateToDoItemCommand(itemId, "Need to do this"));
        commandGateway.send(new MarkCompletedCommand(itemId));
    }
}

If you run the application, you won’t see much. But the Event Store did store the Events. You should see an events directory created, with a number of files (one for each time you ran the application). If you open the file (as a text file), you’ll find an XML representation of each of the Events.

6 Define an Event Listener

Now that we have our Command Handling component up and running, it is time to do something with the Events it produces. Whether you use Spring or “plain” Java, that’s easy.

Let’s start by creating a class that handles some events:

public class ToDoEventHandler {

    @EventHandler
    public void handle(ToDoItemCreatedEvent event) {
        System.out.println("We've got something to do: " + event.getDescription() + " (" + event.getTodoId() + ")");
    }

    @EventHandler
    public void handle(ToDoItemCompletedEvent event) {
        System.out.println("We've completed a task: " + event.getTodoId());
    }
}

The annotations tell Axon that the method is an Event Handler. The parameter defines the type of Event it is interested in.

Let’s wire the Event Handler in our infrastructure.

Programmatically - The pure Java way

In the ToDoItemRunner, add this line of code just prior to the Commands being sent on the gateway:

AnnotationEventListenerAdapter.subscribe(new ToDoEventHandler(), eventBus);

Axon will wrap the bean into an Event Listener and invoke the annotated methods when Events are received through the Event Bus passed as the second parameter.

Spring - Using an XML Application Context

Adding the following two lines of XML will get you going:

    <axon:annotation-config />
    <bean class="org.axonframework.test.sample.ToDoEventHandler"/>

The first tag is needed only once, regardless of the number of Event Handler beans you register. It tells Axon to automatically check all beans in the Application Context for @EventHandler and @CommandHandler annotations. This works very nicely in combination with Spring’s component scanning capability.

The second tags simply creates an instance of the handler. Axon will detect the annotations and do the necessary.

7 Wrapping up…

Although we only have a very basic application, we’ve used the majority of building blocks. To get this running in a real application, you might need to configure a transaction manager on the Command Bus and a transactional Event Store, such as the JpaEventStore.

You also might want to read about Sagas in the reference guide. Once applications become bigger, and multiple Aggregates need to work together to accomplish a task, the Saga will help coordinate tasks between them.

Meanwhile, if you have any questions, don’t hesitate to contact us for help. If you really want a kick-start, visit one of the Axon training sessions, hosted by Trifork.

Enjoy!

8 Migrating from Axon 1.x

The API’s of Axon 1.x are different from those in Axon 2. To migrate, you’ll need to make changes to the code, and to convert the Event Store to the new (better performing) format.
Check out the migration guide for a walk-through of the changes you need to make.