The Concursus Programming Model: Under the Hood

by Dominic Fox
April 29, 2016
by Dominic Fox
April 29, 2016

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 TupleSchemas:

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 EventTypes and TupleSchemas (based on method signatures), and converting method invocation arguments into Tuples in order to construct Events (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.