Event handlers process events by creating a new aggregate or updating an existing one. The Eventuate client framework for Java supports a number of ways to implement event handlers. The simplest approach is to use the high-level API.
The high-level API hides a lot of the boilerplate code that you would otherwise have to write. When using this API you define one or more event handler Spring/Micronaut beans. Each bean defines one or more event handler methods, one for each type of event that the handler processes. The framework subscribes to the events and dispatches each event to the appropriate event handler method.
Here is an example of an event handler class.
It defines two methods, debitAccount()
, which handles MoneyTransferCreatedEvents
, and creditAccount
, which handles DebitRecordedEvents
.
@EventSubscriber(id="accountEventHandlers")
public class AccountWorkflow {
@EventHandlerMethod
public CompletableFuture<?> debitAccount(EventHandlerContext<MoneyTransferCreatedEvent> ctx) {
MoneyTransferCreatedEvent event = ctx.getEvent();
BigDecimal amount = event.getDetails().getAmount();
Aggregate.EntityId transactionId = ctx.getEntityId();
Aggregate.EntityId fromAccountId = event.getDetails().getFromAccountId();
return ctx.update(Account.class, fromAccountId,
new DebitAccountCommand(amount, transactionId));
}
@EventHandlerMethod
public CompletableFuture<?> creditAccount(EventHandlerContext<DebitRecordedEvent> ctx) {
...
}
}
The @EventSubscriber
annotation specifies the name of the subscription.
Each event handler method is annotated with @EventHandlerMethod
.
An event handler method has a single parameter of type EventHandlerContext
, a generic interface, which is parameterized by the event type.
It has methods that provide access to the event as well as metadata such as the type and identity of the aggregate that published the event.
It also defines save()
and update()
methods that the event handler uses to create or update an aggregate.
An event handler method can return either a CompletableFuture<?>
or void
.
The low-level API is much more flexible but requires you to write much more code.
It consists of an EventuateAggregateStore
interface, which you can inject into your event handling code.
class MyEventHandlingCode {
@Autowired
private EventuateAggregateStore EventuateAggregateStore;
...
}
class MyEventHandlingCode {
@Inject
private EventuateAggregateStore EventuateAggregateStore;
...
}
EventuateAggregateStore
has a subscribe()
method.
This method creates a durable named subscription to one or more event types:
interface EventStoreSubscriptionManagement {
CompletableFuture<?> subscribe(String subscriberId, Map<String, Set<String>> aggregatesAndEvents, SubscriberOptions subscriberOptions,
Function<DispatchedEvent<Event>, CompletableFuture<?>> handler);
}
The subscriptionId
parameter specifies the name of the subscription and the events to subscribe to.
The aggregatesAndEvents
specifies a set of event types for each aggregate type.
Since the process of establishing the subscription is asynchronous, it returns an CompletableFuture<?>
.
The handler
is invoked for each event.