Open Credo

April 28, 2016 | Software Consultancy

The Concursus Programming Model: State

In a conventional RDBMS-with-ORM system, we are used to thinking of domain objects as mapped to rows in database tables, and of the database as a repository where the current state of every object exists simultaneously, so that what we get when we query for an object is the state that object was in at the time the query was issued. To perform an update, we can start a transaction, retrieve the current state of the object, modify it, save it back again and commit. Transactions move the global state of the system from one consistent state to another, so that the database transaction log represents a single, linear history of updates. We are therefore able to have a very stable, intuitive sense of what it means to talk about the “current state” of any domain object.

WRITTEN BY

Dominic Fox

Dominic Fox

The Concursus Programming Model: State

(this post continues a series on the Concursus programming model – see part 1 first)

The architectural patterns used in highly-scalable distributed systems often sacrifice the stability and simplicity of this model in order to achieve greater performance, fault-tolerance and reliability. When the data for a domain object is asynchronously replicated throughout a cluster, we can no longer guarantee that every read will be synchronised with the most recent write. Transactions may be unavailable, or costly and complex to implement in a distributed fashion. If we use event sourcing patterns, and record the entire event history of each object in an immutable, append-only log, then we may have to deal with events that arrive out-of-order. “Current state”, in a running system, is then relative and provisional: a future update may change our view of what the state of an object was at a moment in the past.

The usual pattern in event sourcing is to “roll up” what we know of an object’s event history to date into a representation of its state at a particular moment in time. That representation may then be cached, and subsequent events applied to it to bring it further up-to-date. If an event timestamped prior to the snapshot instant arrives, then either it must be discarded or the whole history may need to be recalculated to take it into account. “Current state” is therefore always “state at time T”, and “as far as we know based on the events observed up until now”.

The Concursus programming model supports this “rolling up” operation via “state classes”, which translate an event history into a series of method calls first constructing and then mutating a Java object. Here is an example state class representing the state of a lightbulb:

public static final class LightbulbState {

        @HandlesEvent
        public static LightbulbState created(String id, int wattage) {
            return new LightbulbState(id, wattage);
        }

        private final String id;
        private final int wattage;
        private Optional screwedInLocation = Optional.empty();
        private boolean switchedOn = false;

        public LightbulbState(String id, int wattage) {
            this.id = id;
            this.wattage = wattage;
        }

        @HandlesEvent
        public void screwedIn(String location) {
            screwedInLocation = Optional.of(location);
        }

        @HandlesEvent
        public void unscrewed() {
            screwedInLocation = Optional.empty();
        }

        @HandlesEvent
        public void switchedOn() {
            switchedOn = true;
        }

        @HandlesEvent
        public void switchedOff() {
            switchedOn = false;
        }

        // getters

    }
}

The initial “created” event is routed to the static factory method, creating an instance of the object; subsequent events are routed (by event name) to the instance methods annotated with @HandlesEvent. A class like this could be used to calculate the total duration a lightbulb has been switched on for at any given time, and hence its power usage during any time period.

A StateRepository interface is used to simplify the process of querying for the event history of a particular object, and using it to instantiate a particular state class:

StateRepository lightbulbStateRepository = DispatchingStateRepository
    .using(eventSource, LightbulbState.class); 
Optional lightbulbState = lightbulbStateRepository
    .getState(lightbulbId, Instant.now());

It’s worth noting that a particular kind of object can have more than one state class associated with it, with each class representing a different observable characteristic of the object and handling a different subset of its events. A Concursus state class is not a “domain object” in the traditional sense, but a way of building a representation of state, which may be customised to some particular purpose.

In the next post, we’ll look under the hood at how Concursus represents events internally, and the mechanisms that are used to map between method parameters and persisted event data.

 

This blog is written exclusively by the OpenCredo team. We do not accept external contributions.

RETURN TO BLOG

SHARE

Twitter LinkedIn Facebook Email

SIMILAR POSTS

Blog