April 29, 2016 | Software Consultancy
In the previous two posts (part 1, and part 2), we looked at how Concursus uses method mapping to generate events from method calls on proxies, and to dispatch events to matching methods on event handlers and state class instances. This approach provides a concise, convenient client API to the Concursus event system; however the core of the system defines events and event-handling mechanisms without reference to any of the reflection-based machinery used to implement this API. It is perfectly possible (if comparatively cumbersome) to use a Concursus event store to read and write events without using reflection. In this post I’ll show how this is done, continuing with the “lightbulb” example introduced previously.
WRITTEN BY
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.
Agile India 2022 – Systems Thinking for Happy Staff and Elated Customers
Watch Simon Copsey’s talk from the Agile India Conference on “Systems Thinking for Happy Staff and Elated Customers.”Lean-Agile Delivery & Coaching Network and Digital Transformation Meetup
Watch Simon Copsey’s talk from the Lean-Agile Delivery & Coaching Network and Digital Transformation Meetup on “Seeing Clearly in Complexity” where he explores the Current…When Your Product Teams Should Aim to be Inefficient – Part 2
Many businesses advocate for efficiency, but this is not always the right goal. In part one of this article, we explored how product teams can…