2013-09-08

I have been experimenting with event sourcing in Clojure and in Java, but previously I simply used an in-memory store. In this post I’ll describe how to use Greg Young’s EventStore from Clojure.

The EventStore is written in .NET and runs on Mono. However, I didn’t bother to get it running on my Mac, so I simply used my Windows gaming machine. You could of course run it in a virtual machine or on Linux. There are a couple of things to note when starting the EventStore:

Run as Administrator

Use a special flag to enable projections

Specify IP address to access from another machine, ie don’t bind to 127.0.0.1

This is how I started it: (replace 192.168.0.16 with the actual ip address!)

I will be using AtomPub API of the EventStore. At first I was confused by the order of the events in the feed, as I expected something similar to what is proposed in REST in Practice but I got a reasonable explanation from the EventStore team. The feed contains the most recent events, which means that the events that was generated first are actually last in the collection. That is, the feed is in reverse order to the time when the events were generated. To rebuild the current state of an aggregate I need to first load the last page in the feed, then keep loading the previous page until all events have been loaded.

I’m using clj-http for HTTP calls and cheshire for JSON parsing. The code for this blog post is available as a branch of the original rock-paper-scissors-in-clojure repository. I called the branch atom since most code actually deals with the atom parsing and nothing specific to the EventStore.

I have created an example event stream after creating a game and both players making their move. Here is the full stream and here is a single create game event. It might be useful to open in another window as you follow along.

Parsing Atom feeds

A Clojure program is typically built from small functions. So, lets start by looking at a small helper function to find the link with a certain relation attribute, for example previous or last. This will be of course be used to parse the links section in the full stream.

Then we actually need to do some work. When loading the first page of the feed for a event stream there are three different cases:

1. The stream does not exist

2. The stream exists, but has only a single page

3. The stream exists and has multiple pages

Notice that EventStore uses the convention that a non-existing stream has version -1.

Then we need to traverse all the previous pages:

Notice that a lazy sequence of events are returned (map is also lazy!). This means that only the events that are requested from the stream actually get loaded. In this case it is perhaps not so useful since the first thing we are going to do is to fold over all events to get the current state of the aggregate.

One of the coolest features of the EventStore is the support for projections (see below for an example!). However, this requires that our events are serialized as JSON. It also requires that we specify the eventType and a unique eventId. Unfortunately, EventStore does not allow . in type names which means we have to replace them in the Java package name.

Lets try this in the REPL:

Looks good! To parse this we need some magic:

All that remains is to implement my own EventStore protocol which now is trivial:

As you can see from the append-events function it is really simple to call a REST api using Clojure!

Projection

Finally I wanted to try the projection feature. Here is a projection that calculates the number of games currently in progress:

Lets test it:

Some things to note as projections are not yet so well documented:

I use the fromCategory feature (another example) which goes through all streams with the prefix “game-”.

I use a map to store the state. You can of course use any data structure you want. In this case a simple number would also work fine.

Conclusion

Changing how the events are stored will not at all affect the domain code or the framework. Instead this is nicely abstracted by the EventStore protocol and it is simple to implement this protocol with persistent storage. Using Greg Young’s EventStore has been very easy and I really like the support for projections!

Show more