Configuration

This page describes how you can configure your Aggregate model.

Aggregate configuration

Core concepts within the Command Model are the Aggregates that are implemented. To instantiate a default Aggregate configuration, you do the following:

  • Configuration API

  • Spring Boot

Configurer configurer = DefaultConfigurer.defaultConfiguration()
       .configureAggregate(GiftCard.class);

The @Aggregate annotation (in the org.axonframework.spring.stereotype package) triggers auto configuration to set up the necessary components to use the annotated type as an aggregate. Note that only the aggregate root needs to be annotated.

Axon will automatically register all the @CommandHandler annotated methods with the command bus and set up a repository if none is present.

// ...
import org.axonframework.spring.stereotype.Aggregate;
// ...
@Aggregate
public class GiftCard {
    @AggregateIdentifier
    private String id;

    @CommandHandler
    public GiftCard(IssueCardCommand cmd) {
       apply(new CardIssuedEvent(cmd.getCardId(), cmd.getAmount()));
    }
}

Registering a command handler

Often the command handler functions are placed directly on the aggregate. When this approach is taken, simply registering the Aggregate as described above is sufficient for all its command handler methods to be registered too.

External Command Handlers however do require direct registration as being a command handler, which is shown in the following sample:

  • Configuration API

  • Spring Boot

Given the existence of the following Command Handler:

public class GiftCardCommandHandler {

    private final Repository<GiftCard> giftCardRepository;

    @CommandHandler
    public void handle(RedeemCardCommand cmd) {
        giftCardRepository.load(cmd.getCardId())
                          .execute(giftCard -> giftCard.handle(cmd));
    }
    // omitted constructor
}

The following is needed to register a GiftCardCommandHandler as being a Command Handler:

Configurer axonConfigurer = DefaultConfigurer.defaultConfiguration()
    .registerCommandHandler(conf -> new GiftCardCommandHandler());

Or, a more general approach to registering all types of message handlers in a component can be used:

Configurer axonConfigurer = DefaultConfigurer.defaultConfiguration()
    .registerMessageHandler(conf -> new GiftCardCommandHandler());

When using Spring Boot, simply specifying the Command Handler as a bean is sufficient:

@Component
public class GiftCardCommandHandler {

    private final Repository<GiftCard> giftCardRepository;

    @CommandHandler
    public void handle(RedeemCardCommand cmd) {
        giftCardRepository.load(cmd.getCardId())
                          .execute(giftCard -> giftCard.handle(cmd));
    }
    // omitted constructor
}
Duplicate Command Handlers

As specified in the Messaging Concepts section, a command always has exactly one destination. That means there should only be a single Command Handler method for any given command.

By default, when a duplicate Command Handler method is registered, the last registration will be kept and a warning is logged. This behavior can be adjusted by specifying a different DuplicateCommandHandlerResolver, as is described in the Runtime Tuning section.

Command model repositories

The repository is the mechanism that provides access to aggregates. The repository acts as a gateway to the actual storage mechanism used to persist the data. In CQRS, repositories only need to be able to find aggregates based on their unique identifier. Any other types of queries should be performed against the query database.

In Axon Framework, all repositories must implement the Repository interface. This interface prescribes three methods: load(identifier, version), load(identifier) and newInstance(factoryMethod). The load methods allows you to load aggregates from the repository. The optional version parameter is used to detect concurrent modifications (see Conflict resolution). newInstance is used to register newly created aggregates in the repository.

Depending on your underlying persistence storage and auditing needs, there are a number of base implementations that provide basic functionality needed by most repositories. Axon Framework makes a distinction between repositories that save the current state of the aggregate (see Standard repositories), and those that store the events of an aggregate (see Event Sourcing repositories).

Note that the Repository interface does not prescribe a delete(identifier) method. Deleting aggregates is done by invoking the AggregateLifecycle.markDeleted() method from within an aggregate. Deleting an aggregate is a state migration like any other, with the only difference that it is irreversible in many cases. You should create your own meaningful method on your aggregate which sets the aggregate’s state to "deleted". This also allows you to register any events that you would like to have published.

  • Configuration API

  • Spring Boot

Configurer configurer = DefaultConfigurer.defaultConfiguration()
        .configureAggregate(
            AggregateConfigurer.defaultConfiguration(GiftCard.class)
                .configureRepository(c -> EventSourcingRepository.builder(GiftCard.class)
                .eventStore(c.eventStore())
                .build())
        );

To fully customize the repository used, you can define one in the application context. For Axon Framework to use this repository for the intended aggregate, define the bean name of the repository in the repository attribute on @Aggregate Annotation. Alternatively, specify the bean name of the repository to be the aggregate’s name, (first character lowercase), suffixed with Repository. So on a class of type GiftCard, the default repository name is giftCardRepository. If no bean with that name is found, Axon will define an EventSourcingRepository (which fails if no EventStore is available).

@Bean
public Repository<GiftCard> repositoryForGiftCard(EventStore eventStore) {
    return EventSourcingRepository.builder(GiftCard.class).eventStore(eventStore).build();
}

@Aggregate(repository = "repositoryForGiftCard")
public class GiftCard { /*...*/ }

Note that this requires full configuration of the Repository, including any SnapshotTriggerDefinition or AggregateFactory that may otherwise have been configured automatically.

Standard repositories

Standard repositories store the actual state of an aggregate. Upon each change, the new state will overwrite the old. This makes it possible for the query components of the application to use the same information the command component also uses. This could, depending on the type of application you are creating, be the simplest solution. If that is the case, Axon provides some building blocks that help you implement such a repository.

Axon provides one out-of-the-box implementation for a standard Repository: the GenericJpaRepository. It expects the Aggregate to be a valid JPA Entity. It is configured with an EntityManagerProvider which provides the EntityManager to manage the actual persistence, and a class specifying the actual type of aggregate stored in the repository. You also pass in the EventBus to which events are to be published when the aggregate invokes the static AggregateLifecycle.apply() method.

You can also easily implement your own repository. In that case, it is best to extend from the abstract LockingRepository. As aggregate wrapper type, it is recommended to use the AnnotatedAggregate. See the sources of GenericJpaRepository for an example.

Event Sourcing repositories

Aggregate roots that are able to reconstruct their state based on events may also be configured to be loaded by an event sourcing repository. Those repositories do not store the aggregate itself, but the series of events generated by the aggregate. Based on these events, the state of an aggregate can be restored at any time.

The EventSourcingRepository implementation provides the basic functionality needed by any event sourcing repository in the Axon Framework. It depends on an EventStore (see Event store implementations), which abstracts the actual storage mechanism for the events.

Aggregate factories

The AggregateFactory specifies how an aggregate instance is created based on a given event stream.

This makes the AggregateFactory a configurable component for the EventSourcingRepository only. Furthermore, it is only invoked for existing aggregate event streams, as an event stream is required. Hence, the command constructing the aggregate will not trigger an AggregateFactory invocation.

Axon Framework comes with two AggregateFactory implementations that you may use. If they do not suffice, it is really easy to create your own implementation.

GenericAggregateFactory

The GenericAggregateFactory is a special AggregateFactory implementation that can be used for any type of event sourced aggregate root. The GenericAggregateFactory creates an instance of the aggregate type the repository manages. The aggregate class must be non-abstract and declare a default no-arg constructor that does no initialization at all.

The GenericAggregateFactory is suitable for most scenarios where aggregates do not need special injection of non-serializable resources.

SpringPrototypeAggregateFactory

Depending on your architectural choices, it might be useful to inject dependencies into your aggregates using Spring. You could, for example, inject query repositories into your aggregate to ensure the existence (or nonexistence) of certain values.

To inject dependencies into your aggregates, you need to configure a prototype bean of your aggregate root in the Spring context that also defines the SpringPrototypeAggregateFactory. Instead of creating regular instances of using a constructor, it uses the Spring Application Context to instantiate your aggregates. This will also inject any dependencies in your aggregate.

Note that although you can inject dependencies as global fields of an aggregate, you can also inject them as parameters. When it comes to parameters added to message handling methods, Axon uses the so-called Parameter Resolver support to inject your beans. As such, the SpringPrototypeAggregateFactory does not play any role to inject dependencies to message handling methods.

Implementing your own AggregateFactory

In some cases, the GenericAggregateFactory just doesn’t deliver what you need. For example, you could have an abstract aggregate type with multiple implementations for different scenarios (for example, PublicUserAccount and BackOfficeAccount both extending an Account). Instead of creating different repositories for each of the aggregates, you could use a single repository, and configure an AggregateFactory that is aware of the different implementations.

The bulk of the work the aggregate factory does is creating uninitialized aggregate instances. It must do so using a given aggregate identifier and the first event from the stream. Usually, this event is a creation event which contains hints about the expected type of aggregate. You can use this information to choose an implementation and invoke its constructor. Make sure no events are applied by that constructor; the aggregate must be uninitialized.

Initializing aggregates based on the events can be a time-consuming effort, compared to the direct aggregate loading of the simple repository implementations. The CachingEventSourcingRepository provides a cache from which aggregates can be loaded if available.