Aecor — Purely functional event sourcing in Scala. Part 3

Hello! This is a series of posts about Aecor — a library for building eventsourced applications in Scala in purely functional way. If this is the first time you see this series, I highly recommend to start with Introduction.

In previous posts we learned everything about eventsourced behaviors, constructed and refined one for the Booking entity. Now it’s finally time to see it in action. In this post we’ll:

  • see how you can launch your behavior with Aecor;
  • get a quick overview of how it works on the surface;
  • discuss some theoretical topics, related to running eventsourced entities in a distributed system.


Let’s start with a high-level picture.

Aecor has a notion of a Runtime, which is just an infrastructure platform, where behaviors can be deployed. For example, you would need to store the events somewhere — this is one of the responsibilities of an Aecor runtime.

As you have seen in previous posts, Aecor behaviors are completely unaware of any infrastructure concerns. This is pretty much inline with famous onion architecture: behaviors belong to the domain layer and runtime is out there in the infrastructure layer.

I think it’s pretty valuable, because you don’t always have such clean separation of layers. If you ever tried to do eventsourcing with naked akka-persistence, then you know what I mean.

When you deploy an entity onto a runtime, it looks like this:

This diagram should help you visualize things as we go through the post. 

At the moment of writing, Aecor provides an akka-cluster based runtime, which comes in two flavors:

  • Akka Persistence Runtime, which is an older one and uses akka-persistence under the hood.
  • Generic Akka Runtime, which is more flexible and depends on akka-cluster only.

While it can be easier to start with Akka Persistence runtime (especially if you have done akka-persistence before), I would recommend looking at Generic runtime first. It has some nice advantages, that we’ll talk about later.

Now let’s break the runtimes down into essential components. There are three of them: the journal, consensus and wire protocol.

The Log

Eventsourcing can’t exist without a log, so the first thing you’ll need is the place to store events — the journal.

Event logs existed long before computers were invented

There are a lot of options for underlying storage technology: it can be a specialized solution (e.g. EventStore), or you can pick some general purpose database, like PostgreSQL or Cassandra. The choice depends on performance and operational requirements for your system. What’s important is that semantically journal has to behave as an append-only log.

Journal will need some kind of serialization protocol for your events. It’s a very important topic in eventsourcing, because events, being immutable, require schema evolution to be taken care of. And with akka, serialization is especially easy to get wrong.

Comprehensive guide to Akka serialization

So storage and serialization. If you go with akka persistence runtime, then there’s a variety of plugins for different storage backends. You won’t need anything special to make it work with Aecor — just configure the plugin inline with it’s documentation. Aecor will pick it all up the same way a raw akka-persistence app would do.

On the other hand, generic akka runtime doesn’t know anything about storage or eventsourcing. It’s actually orthogonal: you can partition any kind of tagless final behavior over an akka-cluster using this runtime. 

To deploy eventsourced behaviors on a generic runtime you’ll still need a journal, and Aecor has something to offer here. We’ll see later how you can wire it all up.

Single Writer

I go into some theory here, so feel free to skip this section if you’re familiar with this concept.

Next thing we’d like to have from our runtime is single writer guarantee. In simple words it means, that at any point in time in the whole cluster there is maximum one instance of each specific entity, that can process commands and write events to the log. There should be no concurrent command processing and no concurrent writes to the log for any single entity instance.

For example, in such a runtime if we issue (possibly in parallel) some commands for booking with id “42”, such runtime guarantees that all commands will be handled by a single node, as long as it’s alive. Also, “booking 42” command handler on this node has to process commands strictly one by one, with no concurrency involved.

It’s a very very strong guarantee, which is called serializability of changes (or serializable isolation in database world). It’s extremely hard to provide it globally, but here it’s very local and thus reasonable — we require serializability only within a single entity instance. Such small area of consistency is often called a consistency boundary.

This is why it is so important to think about partitioning when you design your entities. If you want your system to scale, what should grow is the amount of consistency boundaries, not their size.

To get a single writer in a distributed system you need consensus: machines in the cluster have to agree on who is going to be the writer for each entity instance. And this is where akka-cluster comes into picture. It uses a gossip-based consensus protocol to track node membership, which in turn allows to distribute (or shard) entity processing over the cluster.

A lot of new research is going on around consensus protocols these days, thanks to crypto-currency and blockchain frenzy.

After selecting a single node to run the handler, the “no concurrency” property is further ensured by wrapping the handler into a single actor. Actor mailbox guarantees that messages are processed one by one.

Just to clarify, actors are not the only way to do this — you can use an in-memory message queue to do the same thing in a more functional and typesafe manner. And Aecor uses akka not because of actors: it’s the Scala-native consensus which makes akka-cluster invaluable here.

Let’s wrap this section up. There’s a single important reason for Aecor to use akka-cluster under the hood: it allows to retain strong consistency for every entity instance even in a distributed deployment.

Both production-ready runtimes of Aecor rely heavily on akka-cluster. But luckily, except for cluster configuration, it’s completely opaque. Aecor wraps it up nicely in purely functional, typesafe API.

Wire protocol

Last requirement for a distributed eventsourcing runtime is a messaging protocol. The following scenario will illustrate why it’s needed.

Let’s say node A receives a command for booking “42”. Latest cluster gossip say, that booking “42” handler should be executed on another node B. Now node A should transmit the command to node B, where it can be handled. When B is done, it has to hand the result back to A, so that it can fulfill the original request.

So nodes will inevitably have to communicate commands and responses between each other. All of this messaging is nicely handled by akka-sharding, but you’ll need to teach it how to properly encode and decode these calls over the wire.

Aecor doesn’t enforce any specific protocol format, so you can use any format as long as it’s consistent. To do it you just define an instance of WireProtocol for your algebra, where WireProtocol is defined by Aecor as:

Conceptually it’s clear that it’s an encoder/decoder pair. Encoder and Decoder are from scodec library, which is a nice tool to work with binary data in Scala.

Other details are probably not transparent, but we’ll leave them for a later post. We don’t need them right now, because Aecor provides a really nice default wire protocol, which should be enough most of the time.

To get it for your algebra, just mark it with @boopickleWireProtocol macro annotation:

It will generate a WireProtocol instance, using boopickle serialization library. Since these messages are not persisted, serialization can focus on speed and message size. So boopickle is a sane choice here.

If your algebra accepts or returns something special, that boopickle can’t derive a serializer for, macro will fail with an error and you’ll have to manually provide a pickler for that type.

Also, since target algebra that we’re going to deploy is an EitherK, we’ll need to define an scodec.Codec for our rejection type. Given a WireProtocol[M] for algebra M and an scodec.Codec[R] for rejection R you get a WireProtocol[EitherK[M, R, ?[_]] for free.

The following are all manual definitions we need to do for wire protocol to work in our example app:

To see a complete setup, please refer to the github repo.

Deploying to a generic akka runtime

So we went over the components of Aecor runtime. Let’s now finally deploy something.

We’ll start with a more recommended approach, which is to use Generic Akka Runtime. We’ve done some preparation: defined a WireProtocol and configured an akka-cluster. Since we’re not using akka persistence, we’ll need to setup a journal ourselves.

Entity key

But first, it’s finally time to define booking identity. We discussed this a little bit in previous posts: behavior itself doesn’t usually need it’s own identifier to run domain logic. But runtime is a totally different story, so let’s define a trivial BookingKey:

Entity key doesn’t have to be a value class and can easily be composite. But for us a String will work here.

Both the journal, we’re going to define, and Aecor runtime would have to know, how to encode and decode the key. That is required for both storage and sharding to work properly. You’ll just need to define a pair of codecs:

It’s very similar to how you define codecs for Map keys in circe. In this case the codecs are trivial, but for composite keys Aecor provides a small Composer utility (usage example).

Aecor journal

Journals are a nice extension point for Aecor: there’s a simple EventJournal interface, that you can implement to define and plug in your own journal. We’ll use aecor-postgres-journal, which works on top of doobie:

We pass in:

  • a doobie transactor, configured with a PostgreSQL backend;
  • a table name;
  • and a serializer, which in my example uses protobuf. You can check it out here, but it’s just boilerplate, that can be automated with a macro.

There is also tagging parameter, which is configuring the read side of our entity, so we’ll discuss it in Part 4,  dedicated to CQRS.

There’s one nice advantage of Aecor journals over akka persistence. While most akka persistence plugins store all events of all entities into a single table, Aecor entities have completely independent journals.

You can have several entities with journals storing data in different tables, database instances or even different database engines — all deployed to a single Aecor runtime.

Configuring such a setup with akka-persistence plugins would be quite a challenge.


Now we have everything to deploy the entity, let’s see how it’s done:

Ok, what is going on here?

First we define a handle for our runtime, which is kinda trivial. But next line is interesting. Using Eventsourced helper we join together the behavior and the journal. What we get back is a createBehavior function.

It can be viewed as a constructor for an entity behavior: you give it a key, and in return it gives you a behavior handle for corresponding entity instance. You can then use the handler to run commands against this particular instance.

You can also specify a snapshotting policy if you want the runtime to periodically save snapshots of entity state to speedup recovery.

There’s some machinery involved in the Eventsourced smart construtor, but I will save the details for a later post as well.

Now that we have a behavior constructor, what’s left is to distribute it over a cluster. Last statement does exactly that. You can tune sharding-related parameters (e.g. number of shards or passivation timeout) with settings argument.

In return we get a suspended behavior gateway. While the signature is similar to the behavior constructor we’ve seen before, the gateway is much smarter. With a gateway on hand, you can send commands to any node of the cluster, and the runtime will route those commands to different node if it has to. Of course, responses will be routed back to the original node you called the handler from.

The gateway function is suspended, so you just execute that F effect somewhere “at the end of the world” where the application starts. And that’s it — you have an entry point to a fully operational, distributed eventsourced behavior!

It’s a good time to play around with it. Demo repository defines http endpoints to do just that.

Deploying to an Akka persistence runtime

Deployment to akka persistence runtime is almost identical to the generic solution above, just a little simpler — it doesn’t use Eventsourced to run the actions, relying on akka-persistence machinery instead.

Due to similarity and less relevance, comparing to generic runtime, I won’t spend too much time here. You can refer to an exampe wiring in the series repo if you want to. Prior to generic runtime, Aecor was working on akka-persistence-cassandra plugin version 0.59. For simplicity the same version is used in the example. You can pick any other plugin at almost no additional cost.

Getting rid of EitherK

Last thing I would like to show today is a small generalization you can do to your deployment.

As you have seen already, entity gateway we obtain from Aecor runtime returns an EitherK. Let’s quickly refresh what it is — it’s just an algebra transformer, that wraps all the behavior actions into EitherT.

While perfectly usable, it feels too heavy. Since many parts of our system are going to access booking entity, it doesn’t feel right to make them all work with so many transformers.

To make it better we’ll just need to unwrap EitherK, and then unwrap the EitherT in the algebra results. There’s a small conversion function in Aecor, that does exactly that, it’s called Eventsourced.Entities.fromEitherK:

After this transformation the raw type requires a type-lambda to be properly written. So Aecor has an Entities.Rejectable alias, that makes it all look nicer.

And in the end it’s reasonable to define another short alias for the whole entity gateway type. Since it grants access to all instances of this entity, it makes sense to name it in plural (see Bookings[F[_]] alias in the snippet above).

What’s next?

We successfully deployed an eventsourced Booking entity to our cluster. Now, in the spirit of CQRS, we need to project booking events into a view that is optimized for doing simple and efficient queries.

Will do that in Part 4, which is coming next. See you there and thanks for reading!

Aecor — Purely functional event sourcing in Scala. Part 2

Hello! This is a series of posts about Aecor — a library for building eventsourced applications in Scala in purely functional way. If you haven’t yet, you might want to go though Introduction and Part 1 first.

In this post we’re going to:

  • peel off the layers of EventsourcedBehaviour class we’ve seen in the end of Part 1;
  • fine tune the behavior of our Booking entity;
  • learn a new monad transformer along the way.

At some point in writing this post I thought of splitting it in two parts, but in the end decided not to — if it gets too long before we launch something, people might get bored. So this post is a very long read again. I may be a good idea to read it in chunks.

But at least, in the next part it’ll be all about running Aecor behaviors, as planned.

Part 2.1. Understanding Eventsourced Behavior

Previous time we defined Booking entity behavior using plain Scala and a pinch of MTL type classes from Aecor.

It was looking good, so we began to wrap it into something Aecor can launch, which led us to this bit of code.

To put it shortly, it does two things:

  • composes pieces of logic we wrote into a single coherent behavior instance;
  • specializes our MonadAction-ish effect F.

It will take us several steps to completely understand what’s going on. Let’s start with the effect part.

Meet ActionT



As you can see, it’s no joke. So what is ActionT?

The name hints it’s a monad transformer. Monad transformers were invented more than 20 years ago, and these days serve as building blocks to run programs, written using MTL-style (with type classes like MonadState, MonadError, etc.).

As you probably guessed already, ActionT is used to run programs, defined in terms of MonadAction. Let’s do a quick recap of what MonadAction should be able to do (from Part 1):

  • rely on some state of type S to make decisions. We can also read this state.
  • Produce (or append) events of type E as a reaction to commands.
  • Return some kind of result to the caller of the action.

And now directly to the definition of ActionT:

So, first thing I noticed when I initially saw it is S, E, A in type parameters, which is pretty cool, because “Aecor” translates as sea or ocean from Latin.

Now back to the features we want from ActionT. The unsafeRun signature is pretty cryptic so let’s break it up. It’s a function of three arguments:

  • S
  • (S, E) => Folded[S]
  • Chain[E]

You should remember the Folded type from the previous post. Given these three arguments, unsafeRun function returns a value of type F[Folded[(Chain[E], A)]].
For those of you who haven’t heard of Chain yet, it’s a collection from cats that does both append and prepend in constant time. You can freely replace it with List for the purposes of this series.

Let’s draw what we’ve got and solve the puzzle:

Now, given what we know about MonadAction and by substituting type parameters with what they represent, it should be rather clear:

“Give me initial state S, a way to fold events E into S and a log of already happened events, and I will run some action that will return an A and maybe produce some more events. So in the end I’ll return new a new (possibly amended) event log and the A. All of that happens under effect F.”

So it’s actually the same command handler in disguise! There’s a twist though. It’s a composable command handler, by which I mean you can chain them one after another.

It’s possible because of the third parameter — the log of already happened events. Of course, it’s not the whole history of the entity. These are the events that have happened up to this moment of executing the handler.

A picture would be much more expressive in explaining this:


So we can compose actions (or command handlers) into chains, where each individual stage takes the event log from the previous stage, appends it’s own events and passes it down to the next stage.

This is done for two reasons:

  • each action can run this log through the folder to get current state (which is usually needed to do anything meaningful);
  • we need the full log produced by all actions when the whole sequence is complete.

As you see, there are strict rules to how event log Chain[E] should be handled, that’s why the ActionT constructor is private. Not every unsafeRun function would work.

On the other hand, initial state and folder function don’t change throughout execution of the whole chain. They can be completely arbitrary. The public run method on ActionT confirms all of this: you can run your action with any initial state and folder, but you have to start with an empty log.

The M word

After long explanations like this people often tend to say the M word. I’m no exception today.

So first of all, unsafeRun returns an F[_]. And then we somehow expect the next action to use the stuff from inside the F. Which requires a F to be a Monad (and which is actually it’s whole point).

But the composition of actions itself looks monadic as well, and it actually is! So each transition on the picture above is just a call to ActionT.flatMap!

It should all click now for those who are familiar with transformers. Indeed, MonadAction is a Monad, so if we want to run MonadAction programs with ActionT, it has to be a Monad as well. And, like other monad transformers, it’s only a monad when underlying effect F is itself a monad.

As you might remember from Part 1, we found that MonadAction is quite similar to a combination of Reader and Writer. ActionT confirms this similarity: it’s indeed a Reader of initial state S and a Writer of events E.

I hope this “let’s connect the dots” deviation was not too boring for those of you, who got it all straight away. The main takeaway here: ActionT is the engine for MonadAction programs (effectively command handlers), that accumulates produced events along the way.

As an Aecor user, you won’t actually have to deal with ActionT directly, but it surely helps to know, how it works.


Now it’s a good time to look at EventsourceBehavior. As I said, it just gathers the pieces of behavior into one coherent thing:

If it’s the first time you see an abstraction over a tagless final algebra, you might feel this way (I surely did):

But the confusion goes away very quickly. Such type parameter expects something of a shape Algebra[F[_]], for example our Booking behavior algebra would fit nicely as M in EventsourcedBehavior.

So what’t inside?

Two things we’ve just discussed in the ActionT section: initial state S for the behavior and a way to fold events E into the state (the folder function). So let’s focus on the actions portion.

So we have some algebra M and some “raw” effect F. EventsourcedBehavior interprets algebra M in a more complex effect, which is ActionT[F, S, E, ?]. Let’s try these mechanics by hand with a subset of our EventsourcedBooking algebra from Part 1.

We substitute effect I (letter was changed intentionally) with an effect ActionT[F, S, E, ?] for some lower-level effect F:

This code is not what you get exactly, it’s purpose is to just make the point. I removed the implicit requirement in the result, because it’s now satisfied automatically via the MonadAction instance for ActionT.

Well, not exactly — we didn’t touch rejections yet (I simplified the requirement to MonadAction for now). But, putting them aside, we’re good — ActionT provides everything we need. And if we generalize back:

For any algebra M that requires MonadAction, putting it inside EventsourcedBehavior satisfies that requirement.

Cool, so actions is just a named set of behavior specific command handlers. Now we need to tune something here so that it can deal with rejections.


The default way to embed errors into arbitrary effect F is well-known: EitherT monad transformer. It turns F[A] into F[Either[Error, A]], which allows to embed errors into the left channel of the Either.

Aecor faces a more challenging problem, though. For EventsourcedBehavior to support rejections, it has to be able to transform an arbitrary algebra M[_[_]] into some M' so that for every F, M'[F] can embed rejections.

Sounds nuts? It’s actually simpler than it sounds. Let’s look at an example.
Given behavior like this:

we have to wrap all the F‘s into EitherT, so that every method in the algebra can embed rejections, like this:

One more time, but just the diff:


If we try to generalize it into arbitrary algebra M[_[_]], we need this kind of transformation (hope you got used to M[_[_]] thingy a little bit):

And that’s the essence. This way you can take any tagless final algebra and “teach” it to work with errors.

To save some typing and get better type inference, Aecor has EitherK:

Please, note, that cats library has it’s own EitherK class, which is a completely different thing. Whereas is just a coproduct, where each channel is under some effect, is a sort of higher-kinded monad transformer.

Naming the latter EitherTK or something alike would resolve the name clash, but EitherK is simpler to type and pronounce. I haven’t yet seen a context where both would be used at the same time, so this ambiguity doesn’t feel like a big deal so far.

EitherK really does what is says. Writing EitherK[Algebra, Rejection, F] would give you an Algebra[EitherT[F, Rejection, ?]].

Let’s apply this to EventsourcedBooking and, out of curiosity, see what effect type we’ll end up with.

Please, don’t flip your table and leave: you won’t have to type those signatures when using Aecor. We’re just unwrapping it all to get a deeper understanding.

In essence, this is an algebra of command handlers (powered by ActionT with respective state and event types), that can fail with errors of type BookingCommandRejection, (capability provided by EitherT transformer). This combined effect gets a valid MonadActionReject instance, and completely satisfies the initial requirements for EventsourcedBehavior.

Looking at it again

Remember where we started? We assembled EventsourcedBehavior for Booking algebra:

Now it should make sense to you. We’re building an ActionT-based behavior with rejection support via EitherK.

The only missing bit is optionalRejectable smart constructor, which is nothing more than sugar:

  • it allows us to use BookingState.init and BookingState#handleEvent without lifting result to Option;
  • it requires an EitherK based algebra, which helps to drive the type inference, so that we only need the outer level type ascription.

Here it actually makes a lot of sense to download the ticket-booking project (or aecor itself), open it in IDE and click through definitions to see how types match up.

Phew… I bet this was intense and took a bit of energy. It’s a good time to take a break before we look at how we can tune an EventsourcedBehavior.

Part 2.2. Tuning EventsourcedBehavior

So now that we’re somewhat comfortable with EventsourcedBehavior, let’s see how we can modify it to our needs.

Event metadata

As I mentioned in Part 1, it’s beneficial to separate essential event data from generic metadata. Especially if the latter doesn’t participate in making decisions within the behavior. Let’s see how we can do it in Aecor.

Given behavior that we just constructed, we can call behavior.enrich to get a new behavior that will automatically enrich every event with metadata we specify.

Let’s say we want to store a timestamp along with each event. For enrichment to work we need only one thing — a suspended getter in F:

What you get is the same behavior, but with different event type: instead of plain BookingEvent, you get events of type Enriched[EventMetadata, BookingEvent], which is just a simple product:

For every produced event Aecor will execute generateTimestamp and put the result into the Enriched envelope.

One small thing is missing though. enrich requires that the algebra baked inside EventsourcedBehavior has a FunctorK instance. In plain words it means, that for such algebra M, we can at any time go from M[F] to M[G] given a natural transformation F ~> G.

Providing FunctorK instance is mostly a mechanical process, and thanks to cats-tagless project we can get it for free. We just have to annotate our tagless final algebra with @autoFunctorK:

That’s it. It was really simple, and we get metadata supply without polluting our clean events with irrelevant things. Power of composition is unlimited 🙂

Do you even lift, bro?

Now a plot twist. The whole team realizes we forgot to add booking expiration functionality.

Well, it’s not that bad, we’re still working out the behavior. So on the entity level it should be rather simple (you might want to refresh the Booking algebra we defined in Part 1).

First, we need an expiration event. Also, when booking is initially confirmed, we should store the exact moment in future when it will expire (if not paid or canceled by that moment):

Second, we need to make corresponding adjustments in the algebra: add an expire action and tweak confirm action to receive the expiration deadline.

The expiration deadline will come from an external ticket management system, so for the Booking behavior it’s just an argument. We’ll also add an expiresAt: Option[Instant] to the BookingState — we’ll need it to validate any attempts to expire the booking too early.

Last thing to do is to implement expire action in the EventsourcedBooking version of Booking algebra. And this is where we’ll get stuck.

To check that expiration doesn’t happen to soon, we’ll need to get current system time, and check that it’s already past the expiresAt deadline. Getting current time is a side-effect, so we’ll need some kind of effectful clock. We’ll pick one from cats-effect:

But if we then try to work it all up from here, we’ll have a hard time finding a Clock[I] instance. As we remember, I is a very special effect — it supports command handlers that produce events.

But we need just a simple clock, is there a way to avoid all of this additional complexity?

For sure. If we need a simple effect, let’s just add it:

Good. But there’s another problem. Once you execute your clock, you get an F[Instant]. But you can’t just flatMap it into an I[Something]F and I are completely unrelated effects. If there was a way to go from F[Instant] to I[Instant], then everything else would work as it did before.

It turns out that there is such a way, and it seems logical. After all, F is a much simpler effect, and the more powerful I should be able to embed F values.

For such behaviors Aecor provides MonadActionLift and MonadActionLiftReject type classes. These are just extensions over corresponding type classes we already know:

This is all we need. Now we can liftF the result of our clock into I, and everything will work again. Let’s see the final implementation:

Types will work their way up now. To see how it looks as a whole, please refer to the repo.

One may ask here:

So we embedded a clock into our behavior. But the same way we could embed any kind of effectful service, right?

Seems like too much power, where’s the line?

A fair question. Although it’s possible, I would keep it to simple local side-effects like Clock or Logger. Embedding something more complicated doesn’t make a lot of sense — if it’s a database or external request, you don’t get any additional atomicity guarantees anyway. But the downside is significant — you clutter your behavior with some logic, that could be executed elsewhere (and the results would be then passed to behavior as simple arguments).

Still, the possibility is there, so if you make a really good case — go for it! I can imagine some external validation being implemented this way. In my opinion, as long as this external service doesn’t populate your events, you’re fine.

Time is out

Well, not really. But this is the last tweak I’ll cover today. It’s a really simple and neat example of how powerful these effect-polymorphic behaviors are.

It’s not really relevant in this case, but let’s say we have a behavior that can take long to handle commands. Maybe it does some really heavy calculations or goes into database for validation purposes.

In this case we’d want to limit the maximum response time and get a timeout if the command processing takes more than 2 seconds. With a less composable solution we’d have to bake it inside the behavior, or handle it on the outer level.

With Aecor you keep your original behavior intact and just run it through a natural transformation to get a modified version. This requires mapK, and EventsourcedBehavior is a FunctorK as long as underlying algebra M is a FunctorK too.

Let’s define a natural transformation that implements a timeout cutoff:

We leverage some goodies from cats-effect here, namely Concurrent.timeoutTo. This natural transformation takes any Concurrent effect F and produces an effect of the same type F, where any action that takes more than 2 seconds will raise an error. We can do that since Concurrent extends MonadError.

The only thing left to get a timed-out behavior is to run the original behavior through the given natural transformation:

And that’s it. Pretty concise, isn’t it?


This was a really dense post with a lot of code to crunch. I really appreciate the time you spent reading and hope you extracted some value out of it.

Next time we’ll finally launch our behavior on a real cluster with a real event journal. Stay tuned.

Aecor — Purely functional event sourcing in Scala. Part 1

Hello! This is a series of posts about Aecor — a library for building eventsourced applications in Scala in purely functional way. If you haven’t yet, you might want to go though Introduction first.

In this post we’ll explore entity behaviors in general, and how to make eventsourced behaviors with Aecor. I’ll also delve into design practices and try to answer arising questions, so grab a coffee — it’s not a quick read 🙂

Part 1. Defining entity behavior

As we agreed in the introduction, we’re building a ticket booking system.

Booking tickets is the core domain of our imaginary business, and with all this complexity and inherent temporality, “Booking” entity is a good candidate to be eventsourced.

But why exactly Booking? How does one come up with this decision? Let’s stay a little bit on this topic.

Picking entities

Event sourcing works really well with Domain Driven Design, and the term Entity comes from DDD. In the famous “Blue book” entity is defined as something that:

  • has a distinguished identity, that allows to differentiate two instances of an entity, even if all their attributes are the same
  • usually obey some form of lifecycle, according to business rules.

One can easily define several entities in a ticket booking context, and Booking is what first comes to mind:

  • it must have some unique identifier, so that a client can refer to it at any time (identity)
  • as the booking process goes forward, it goes through several distinct states (lifecycle)

So we selected Booking as one of our entities. This alone doesn’t imply event sourcing — in classic DDD entity is backed by a regular CRUD-ish repository.
But if we see value in eventsourcing some part of the system, entities are usually a natural fit to have a consistency boundary wrapping them around.

Eventsourcing an entity (or several entities, wrapped into an Aggregate) usually gives the best trade-off between granularity (which gives scalability) and consistency, meaning that most of our business invariants can be checked within single consistency boundary.

Behavior interface

So we decided to eventsource our booking entity. Time to define some behavior for it!

First step, which actually doesn’t require Aecor or any other library, is a Tagless Final algebra for your entity. Let’s put it like this:

If you’re not familiar enough with Tagless Final, there’re lots of good posts on the web. I can recommend this one by @LukaJacobowitz, or my own Writing a simple Telegram bot with tagless final, http4s and fs2.

So what do we see here that our booking can do?

  • We can place a booking for 1 or more specific seats in a concert on behalf of a client.
  • Booking can be confirmed, which means, that seats are reserved and prices are determined. We define an explicit confirmation step here, because actual concert data management and seats reservation is done in another system. Confirmation is going to happen using asynchronous collaboration with that system.
    That system also manages pricing, so when booking is confirmed, seats become tickets — in our case a ticket is just a seat with price attached.
  • By the same token, if something goes wrong (e.g. seats are already reserved), booking is denied with a reason.
  • Client can cancel the booking any time.
  • Receive payment is an obviously important lifecycle action for booking.
  • And our entity will expose some parts of it’s internal state, namely status and tickets (optional, because there’re no prices until the booking is confirmed).

Just several lines of code, but quite a bit of thought and effort. And also questions! I’ll try to answer ones that most probably arise at this point.

This algebra definitely looks like something with internal state. Why so?

It’s true, and for reasons:

  • We focus on behavior. Internal state that will fuel it is secondary, and we don’t want to tie the behavior algebra to it.
  • When some other component calls an action of this behavior, it shouldn’t be bothered with booking internal state either.

I usually think of it like this: an instance of this Booking algebra would represent a specific booking entity instance at it’s current state, and the methods are actions that you can perform with that instance.

Why F[Unit] all over the place? And where are the errors? You can’t pay for a denied booking, for example.

Fair questions. Unit here represents some kind of “Ack” response, meaning that the action succeed. Booking will probably change inside, but we don’t care. Returning Unit in this case is very common in Tagless Final.
As for errors — in good traditions of MTL we delegate error handling to our effect F.

By the way, at the moment it looks like most TF algebras out there, where F is going to be something like IO or Task in the end. Spoiler: it won’t be so when we get to eventsourced behavior.

If these actions are for a particular booking, what is place doing here? Don’t we create a new booking by placing it?

This is an interesting one. When you do traditional CRUD, the creation of entity instance is separate from any kind of logic it might have (or not have).
But if we move completely into behavior land, then there’s definitely some kind of business action that brings the entity into existence. In our case this is place action. It’s a an integral verb of our domain and a part of the entity lifecycle, so we treat it accordingly — it belongs to the entity algebra.

Behavior actions, MTL-style

I hope we’re ready to move forward and finally unpack some Aecor typeclasses. Let’s take a look at them.

The core one is MonadAction:

It provides basic building blocks for actions. Aecor action describes how an entity reacts to incoming commands, which makes it very similar to command handler concept. As signatures might have suggested you, actions:

  • rely on some state of type S to make decisions. We can also read this state.
  • Produce (or append) events of type E as a reaction to commands.
  • Return some kind of result to the caller of the action.

So any effect F that can do these things can be used to describe actions and thus have an instance of MonadAction.

We will also need errors. In the context of handling commands an error means that command can’t be executed for current state of the entity. For example, one should not be able to pay for a denied booking. In this case we say that receivePayment command is rejected, and the action resulted with rejection.

Aecor provides a more powerful version of MonadAction, that can work with errors. It’s called MonadActionReject:

It’s related to MonadAction in the same way to how MonadError relates to Monad. Usually, your entities would need rejections, but sometimes there’s no such need — this is where you can get away with a simpler MonadAction.

Before we implement our actions, we’ll have to agree on S, E and R types for our eventsourced booking.


Implementing event sourcing is inherently harder than more traditional state-based approaches. One of the reasons is that in addition to state you will need events (and in our case also rejections).

Mining proper events from the domain is a big topic in itself. Let’s say we already had an eventstorming session with our domain experts and came up with the following events:

BookingPaid and BookingSettled are distinct events, because some bookings are free and can be settled without payment.

Notice, that we’re back to no-dependency mode: these events are completely arbitrary and library agnostic — no marker traits or similar hacks. Maximum composition.

Also, we don’t put any identity information or metadata (e.g. timestamps) here. Aecor provides a way to decouple business-related data from metadata to make events cleaner. We’ll see later, how you can enrich your events with metadata. We’ll discuss identity soon as well.


Next, we’ll need our entity to keep some state inside. We should not fall into a trap of thinking database schemas here. The purpose of this state is not to map into tables or provide efficient queries — it’s part of your domain model, so it should:

  • be readable and use ubiquitous language;
  • be rich enough for expressive command and event handling;
  • support the whole entity lifecycle.

We’ll use the following state for our entity:

tickets is optional, because we don’t have seat prices for the whole life of the booking — we get them with confirmation. A more typesafe way to encode this would be to put a non-optional tickets field in all statuses where the tickets have to be in place. Here for simplicity we just put an option into the state root.

And again — our state is totally library agnostic.

Identity in state and events

A fair question here would be:

You say a lot about identity, but where the hell is the bookingId?

This is a neat idea I first heard from Denis Mikhaylov. It says that in general, entity should not need identity information to handle commands. You definitely need some kind of identifier to route a command the the correct entity instance. But after that business logic doesn’t usually care.

Moreover, when it appears that chosen identifier is still required for business logic, you most probably can decompose it into two parts: pure identity and something that is required for command handlers to work. Then you move the former out of your events and state, keeping only the latter.

I’ve implemented and seen this idea in action, and I find it awesome. Separation of concerns all the way down. Answering the question — we’ll definitely see bookingId later, but it’s not relevant for our behavior.


I won’t spend too much time on rejections. Simple enum is usually enough, but nobody stops you from enriching your rejections with some data. Here’s what we got for booking command rejections:

Implementing Actions

We’re ready to implement actions for our eventsourced behavior. We’ll start by requiring our effect to be a MonadActionReject:

Our ADT’s from previous sections took their respective places, with one quirk: state is wrapped into Option. This is where we get back to the trade-off of having place verb in our behavior algebra. Until the booking is placed, there’s no trace of it in the system, and hence no state.

It’s a common thing in event sourcing: very often there’s some kind of initial event that moves the state from None to Some(...). At this level we have to accept this and express it in our types.

Let’s implement place action:

Let’s walk through this code:

  1. Import MonadActionReject DSL into scope
  2. Use read to get current state of this booking entity
  3. If something is already there, it means that this particular booking was already placed and we can’t re-place it again: reject the command.
  4. If it was not placed, we perform some validation an either reject the command or append a BookingPlaced event.

Congratulations, this is our first command handler!

Aside on MTL.

We can flatMap in F because MonadAction extends Monad. This gives us a lot of power in defining out effectful actions, especially when other effects come into play (we’ll see an example later).

MTL fans could have noticed, that MonadAction[F, S, E] is very similar to a combination of MonadReader[F, S] and MonadWriter[F, E]. Rejections add up to MonadError[F, R]. Notable exception is reset combinator, which adds a remote flavor of MonadChronicle: it allows to drop all the accumulated reactions and start over from a clean slate.

All of this is not accidental — it’s just the nature of command handlers. They have to read state, write events and raise rejections. So MonadAction could probably “extend” these mtl typeclasses… but so far no practical benefit was found and Monad is just enough.

More handlers

Let’s complete the actions for eventsourced booking.

Let’s walk through confirm action. Others are pretty much similar.

  1. Unlike place, confirm runs on existing booking and should be rejected for a booking that was not yet placed. This is handled in status method, that confirmation action calls into.
  2. After booking is confirmed, if tickets are free we can settle the booking immediately. Notice, how regular monadic combinators are used to do that.
  3. Sometimes the handler doesn’t have to do anything and just ack (e.g. double confirmation). ignore alias is defined for a better readability in these cases.

Experienced eventsourcing practitioners would say that this is only half of the story. Our behavior produces events, but we haven’t specified how the state would change in reaction to these events.

Folding events

It’s not a secret that eventsourcing is conceptually just an asynchronous foldLeft on an infinite stream of events. Obviously, we lack a folding function for this to work.

Actually, given the optionality of our our entity state, it makes sense to define two functions:

  • one for initialization, where we go from nothing to something;
  • second for more regular lifecycle transformations, from one existing state to another.

We’ll define both on our BookingState since folding events is one of it’s direct responsibilities:

Here we face an eternal problem of eventsourcing, which is handling illegal folds. Usually lifecycle implies that some events can only happen in particular states. For example, we shouldn’t ever receive a BookingDenied event for a booking that has Settled status.

Command handlers must hold such invariants, so seeing an illegal fold at runtime is a programmer’s error. It’s very hard to navigate this knowledge into the fold function. Especially in a way that compiler would allow us to write only folds that make sense and will actually happen.

It would probably require much more complex type signatures and totally different structure to pull that trick off. The payout is nice but is not worth the effort: for a properly designed aggregate of a normal size code review is enough.

Aecor provides a specialized Option-like type called Folded[A] to account for illegal folds:

You can see it wrapping the fold result in the functions we defined earlier.

⚠️ A timeless warning! 

Never side-effect in your event handlers!

Always worth mentioning. Aecor is as explicit about it as it can be — everywhere it needs a fold function, it’s has to be without effects.

In Haskell that would be enough, but not in Scala. Just keep doing pure FP and you’ll be fine 🙂

Bringing it all together

Now we’re finally ready to wire it all up into something Aecor can launch, which is (you don’t say) EventsourcedBehaviour.

Oh well… I guess this is enough for now. It was a long read, and the signature above screams for a fresh head. So let’s call it a day, and dive into EventsourcedBehaviour next time.

Please, post your feedback in comments and thank you all for reading!