First of all we define an EventType
for a “lightbulb created” event, and a TupleSchema
which defines the names and types of the parameters that can be associated with events of this kind. In this case there is only one parameter, “wattage”, which takes an integer:
EventType createdEventType = EventType.of("lightbulb", VersionedName.of("created", "0"));
TupleSchema lightbulbCreatedSchema = TupleSchema.of(
"lightbulb/created_0",
TupleSlot.of("wattage", int.class)
);
We also create a TupleKey
which provides type-safe access to the “wattage” parameter, given a Tuple
of parameters conforming to our schema:
TupleKey wattage = lightbulbCreatedSchema.getKey("wattage", int.class);
This is all the metadata we need to create a new Event
, which can then be stored directly in our EventStore
:
Event createdEvent = createdEventType.makeEvent(
"id1",
StreamTimestamp.now(),
lightbulbCreatedSchema.make(wattage.of(60)));
InMemoryEventStore eventStore = InMemoryEventStore.empty();
eventStore.toEventOutChannel().accept(createdEvent);
When we want to read the Event
back out of the EventStore
, we need an EventTypeMatcher
that can match event types up with TupleSchema
s:
EventTypeMatcher typeMatcher = EventTypeMatcher.matchingAgainst(
ImmutableMap.of(createdEventType, lightbulbCreatedSchema));
This is because event parameters are normally serialised (e.g. into JSON) in storage, and we need information about parameter types in order to deserialise them again; the TupleSchema
is also used to verify that the stored parameter data matches our expectations. We supply this EventTypeMatcher
to an EventSource
when retrieving events (events whose type is not matched by the supplied EventTypeMatcher
will be ignored):
EventSource eventSource = EventSource.retrievingWith(eventStore);
Event retrievedEvent = eventSource.getEvents(
typeMatcher,
AggregateId.of("lightbulb", "id1"))
.get(0);
Finally, once we have retrieved our stored event from the event store, we can use the TupleKey
we created earlier to read the “wattage” value from its parameters in a type-safe fashion:
assertThat(retrievedEvent.getParameters().get(wattage),
equalTo(60));
All of the method mapping code we saw before is essentially a convenient way of defining EventType
s and TupleSchema
s (based on method signatures), and converting method invocation arguments into Tuple
s in order to construct Event
s (and vice versa). We could if we chose use an entirely different mapping mechanism – for example, one based on bean-like POJOs with properties, getters and setters – to accomplish the same thing. In the next post, I’ll show how Kotlin’s sealed classes can be used to do just that, to provide a clean and convenient alternative API.
This blog is written exclusively by the OpenCredo team. We do not accept external contributions.