Recently Mathias Verraes published several posts in his blog around patterns for Events. This was very fortunate indeed, not only because these are valuable insights for those who architect decoupled applications using Events as a key instrument, but also, quite incidentally, because it gave me the opportunity to streamline my own ideas on Events and asynchronous decoupled architectures in general.
Inspired by some of the ideas he introduces there, I came up with the concept of Story as an implementation of the Event Summary pattern. Let’s see how and why.
Event Summary pattern
One of the patterns that intrigued me more is the Event Summary pattern. In short, this would be an individual Event whose body gathers data already present in the bodies of other, previously dispatched Events, according to some relationship they share that makes them convenient for being dispatched together, and consumed all at once as part of an aggregate.
Let me give you an example. Just imagine a music concert and a service that dispatches a SongPlayed Event every time the band ends playing a song. Now, also imagine another service that only acts on music concerts as they end, for instance, to communicate the list of songs played to a music rights management entity.
Clearly, all the SongPlayed Events dispatched during a particular music concert share a common relationship, which is that they were played that day, in that music concert. This is the fact by which we may found useful to dispatch a summarising message at the end of the concert.
In this scenario, would not be easier (and cheaper) for that latter service to consume just an Event Summary dispatched at the end of the concert, with information gathered whenever a song was played, or even taken from the bodies of those SongPlayed events, instead of consuming all those annoying SongPlayed events one by one?
Likely everyone, even I myself, would answer YES.
Regardless of my affirmative answer, there are some details around this Event Summary pattern that, in my opinion, deserve further attention before we move forward with my proposal for an implementation.
A tale of species
The circumstance that we have got an infrastructure of asynchronous messaging should not drive us to say that every structured message travelling up and down throughout that infrastructure is an Event.
In my opinion, we should define a kind of a taxonomy of messages so that applications may behave one way or another depending of what kind of message they got to have to deal with. In practice, automated services are ready to handle a short list of messages, and silently let the others go away. However, the list of message patterns is growing, and IMO we architects would appreciate to have them organised somehow around their features: headers, body, name, etc.
Following the thread of posts presented by Mathias Verraes in his blog, there are at least two main features which may help us build that taxonomy:
- Fullness, which allows us to classify messages depending on whether they bring all the information available in the context of the message dispatcher, or they just left some data behind for any reason, in particular because stayed unchanged (i.e., their values were the same before and after the fact that the message informs about happened).
- Time Atomicity, which allows us to identify messages as atomic, were they representing a fact occurred in a given instant of time, or not atomic, were they bringing information related to a happening broader in time.
A definition of Event
With all that arsenal on hand, let’s start by streamlining the definition of Event:
Events are full representations of facts that happened in a moment in time.
When we say the Events represent Facts we mean that Events bring what happened in the real world to the application using the terms the application understands. So Events not only transport information, but translate it as well from the common language words we all understand to the specific terms that we would find in the code were we reading it, and that were invented in the process of designing the application.
If we look at the definition of Event under the light of the two message features above:
- All the raw information available in the context of the fact should be included in the body of the Event that represents it. There are operative details to account here, but let’s just say that the context of a fact is all the data that we may collect about it without doing any additional operation, like querying a data source or make some calculation.
- Facts refer to an instant, roughly a date and time, up to the second or as minutely as needed. This usually means that facts got an Event representing them as soon as they end. In English, this is indicated by past participles of verbs.
So, according to these remarks above, it seems clear that messages from the past which are not exhaustive, but selective, when taking data from the fact being represented, should not be considered Events.
This condition of Fullness avoids dispatching Events with only the data any given Consumer would claim for. Consumers must take all or nothing, and simply ignore whatever data they do not need from the body of the Events they consume.
No matter reasonable this Fullness may look, it is just convenient. Actually, it is easy to imagine scenarios in which breaking this Fullness rule is far a better option. Mathias Verraes also talks about this in the post Segregated Event Layers in his series.
Fullness is not Completeness
I think it is worth mentioning that the Fullness condition introduced above refers to every particular message dispatched in the application. Actually we may be dispatching messages which are selective, in the sense that some properties available in the context of the dispatcher at dispatch time are not included in the message body, instead of exhaustive, so that they could not be called Events.
Even though, we may be dispatching an Event whenever a fact happens in the business context mapped in our application via one, or more, services. In this case, we would be talking about Completeness. I took this term also out of Mathias Verraes blog, where he calls it a Completeness Guarantee.
Obviously, it takes much more effort to accomplish that Completeness Guarantee that just to ensure that every message dispatched is full. In my opinion, it is a feature of our applications that is achieved incrementally, in a journey which might likely never reach its end (unless we started up with a requirement for Completeness Guarantee fulfilment in mind from minute zero).
Summary Events is a message pattern which breaks the rule of Atomicity because they represent facts which life spans longer than just an instant. These messages may, or may not, fulfil the Fullness rule. Whenever they do, I propose to call them Stories.
Stories are full representations of facts that spanned during a period of time.
(I must recognise that this is not so an original name: we are living in the times of Snapchat’s stories and Instagram’s instastories.)
The key point in the definition of Stories is the past tense. Nothing can be considered a fact if is still going on, not only because otherwise we cannot know when it ends, but also because we cannot say for sure that it will end.
Therefore, anything that should be triggered in an application during the lifespan of a still ongoing Story, should come out of true facts (for instance, an already played song in a still ongoing music concert), which means an Event. This is how the Event Summary pattern comes implemented with Stories.
In general, uninterrupted periods of time can easily be represented as Stories. However, there are situations in which consistently unique facts exist, even though they might be interrupted. For instance, a Wimbledon tennis match which was interrupted by the rain. That interruption should be included in the Story’s body, in order to make sense of the partial facts, or Events, which are part of it.
In practice, there are (at least) two ways of implementing Stories:
- As a deferred fact.
We know for sure that the facts are going to end some moment in the future, and we keep gathering information out of its context up to the very moment it ends, and then we dispatch the whole Story downstream.
- As a projection produced after the last Event in the Story is dispatched.
An example of a Story: a motorbikes race
Let me finish this long post with an example taken from the company I am currently working at, and consider a motorbikes race of the World MotoGP Championship.
A race has a clear starting at point in time, but it is not possible to know the exact time at which it will end. However, it constitutes a unique fact in time, which makes it eligible to be represented as a Story.
There are plenty of facts that happen in precise an instant during a race, some of them expected (i.e., the first rider crosses the line after completing the first round, etc.), whereas some of them are unexpected (i.e., accidents, rider falls, red flags, etc.). Every time any relevant fact happens, an Event should be dispatched. This is what makes this a Story, instead of simply a collection of data we may produce in anticipation.
Actually, this kind of Story is extremely simple to implement: the data is gathered from the context as it is, with no further operation. And, just at the end of the race, whenever the last rider crosses the line, the Story can be dispatched.
Event Summary pattern revisited
However broad the bunch of real situations in which they may be the rightest message representation of choice, Stories are not a simple aggregation of data. They collect the information available of a longer-than-one-instant fact and bring it into our application.
Going back to the post in the blog of Mathias Verraes where the Event Summary pattern is presented, a daily sales report summarising all the sales in a given day is clearly not a Story because all those sales operations are not related whatsoever, except for the circumstance that they happened the same day.
Did I came up with a name for this third kind of messages? Not with one that convinces me: I’d call them Reports. But let’s leave them for another day.
Photograph credit: https://pixabay.com/users/geralt-9301/