Software messaging patterns and time travel

Messaging is one of these things everyone thinks they use and understand. If you look at events closely, they are fairly simple. Often it’s just a timestamp with few properties. What harm could it really do? To find out you may want to check this little article 45 things that can go wrong with events. Actually… don’t go there! It’s about outdoors events planning. The title sounded scary so I decided to use it here to emphasize my point.

Things really can go wrong when events based system scales up and doesn’t use the eventing/messaging patterns correctly. Its like using screwdriver for nailing planks. I guess with a big enough screwdriver it’s possible but it’s not going to be efficient. There are 3 well known patterns that people tend to mix and match. This isn’t necessarily a bad thing – if you know what you are doing.

We will start with this one:

Events Sourcing

PersonelManifestEntryAdded
{
   EntryId: "d0d18615-829c-4fc7-a12c-26d580e237e6";
   Name: "James Tiberius Kirk";
   Origin: Earth;
   Rank: Commander;
}

PersonelManifestEntryModified
{
   EntryId: "d0d18615-829c-4fc7-a12c-26d580e237e6";
   Name: "James Tiberius Kirk";
   Origin: Earth;
   Rank: Captain;
}

Event sourcing is a special way of modelling state persistence and state changes in a domain of your application. Instead of modelling/storing objects in a relational data store you only store events – representing the change in their state (S1 -> S2 -> S3). So you wouldn’t have a dbo.KanbanBoardTable and a dbo.KanbanBoardCardTable. Instead you would have a series of state change events like BoardCreated->BoardCardAdded->BoardRenamed. Anytime you have to display your Kanban board in a Trello-like-application you need to compile its current state by processing a sequence of related events.

On complexity

We hear a lot about event sourcing among our peers, but we rarely see enterprise application using it. Why is that? Most of the time its due to its complexity and inherently low performance. After all you have to model all of your domain changes with Events like OrderShipped->OrderCancelled->OrderReturned. Now, imagine a more complex domain object like an insurance policy. There can be multiple sections and sets of information related to it. Because of that there may be a lot of ChangeEvents required to recompute its state (this has a potential to be very slow).

There are ways to improve performance by periodically snapshotting our objects. This way when you try to compile its current state, you only load the latest snapshot of an object and then apply event state changes that occurred after the snapshot was made. So while we rarely see this pattern used in an enterprise class software we do see it partially implemented whenever complex logging and the ability to revert objects to their previous state is required (Reverting and replying events does have its own issues – especially when you depend on an external system or user interaction).

A very practical and interesting use case for Event Sourcing are definitely Version Control Systems. Think about it – whenever you are rolling some change back or requesting a particular file revision from some point in time, you are really querying an object state at a particular point in time. VCS systems often use EventSourcing to give their users ability to branch code from any point in time and still keep track of changes history between each branch.

Time-Querying

So, while the style of programming and object modelling may not be very intuitive for most of us, event sourcing does provide some unique benefits. One of which is the ability for “Time-Querying” your whole domain. You could easily reply state of your system or parts of it from any point in time and visualise it in some helpful way to aid customer service or any other problem solving/analysis that you may require.

Another thing worth mentioning is the scalability factor – great benefit baked into most event driven architectures by design.

Lastly, a lot of the time you will hear that you have to use CQRS together with Event Sourcing. CQRS is the notion of having separate models for storing and retrieving information. You should be aware that this is not true and while CQRS can help, it also adds complexity of having way more models than you would ordinarily have – so as always, use common sense.

Here you can find some more details on this pattern.

Domain Events a.k.a Notification Events

StarshipDocked
{
   EntryId: "442a6019-592f-41ea-9964-0f6fa9c394fd";
   StarDate: 50893.5;
   Designation: "NCC 1701";
}

StarshipDepartured
{
   EntryId: "442a6019-592f-41ea-9964-0f6fa9c394fd";
   StarDate: 50896.2;
   Designation: "NCC 1701";
   Destination:
   {
      Name: "Qo'noS";
      Sector: 70;
      Block: 27;
   }
}

While event sourcing will create a rather chatty ecosystem of many fine grated events – notification events in most domains will be fewer and will represent a different, higher level information. Notification event should always carry the information stating that something important to the business domain has just happened. Tricky bit is usually identifying what is “important” and what is part of the business domain.

Important questions

Regardless, we have to be aware of this if we hope to provide a sensible level of decoupling and scalability to our solution. Too many fine grain events over time can make it a messy spaghetti of hidden interconnections and data flows no one understands. To identify weather some action should be encapsulated as our domain event, it is good to go through such checklist:

  • How many subsystems will be (now or in future) interested in the fact that this happened. If its 1 then it’s an indication this may not be that important of an event.
  • Would I immediately understand what this event/action mean? (If I were a non-technical business user)
  • If we would transfer this system to an anolog age would this event still exist? (instead of streaming video we would be mailing a VHS – hope you do not have to google it :-)). Point here is that it is not an event stating that something technical happened like ZipFileDownloaded

So, we wouldn’t create an event stating that the VideoPreprocessingHasBeenCompleted allowing our client to start streaming the latest Netflix original to our customers living room.

PreProcessing and a concept of stream is way to technical and really, there isn’t even a need for an event here…

It’s a synchronous action: RequestStream << WaitForPreprocessing. An important domain event could be “ClientStartedWatchingAMovie” because this is what we are about. We deliver movies to people’s screens.

You can read a bit more about this pattern in Martin Fowler’s article here

Event Carried State Transfer

MedicalRecordChange
{
   EntryId: "15e4cff8-aa75-482b-92ac-91c78d20749f";
   Name: "Annika Hansen";
   Race: Human;
}

MedicalRecordChange
{
   EntryId: "15e4cff8-aa75-482b-92ac-91c78d20749f";
   Name: "Seven of Nine";
   Race: Complicated;
}

Events in this type of messaging pattern carries information about the change made to some system object. Just like in event sourcing. But unlike it, here it’s not used for recreating the current state later.

We talk about Event Carried State Transfer whenever we want to update a number of subscribed clients about a change to the data in a scenario when these clients hold their own copy of our data. This type of architectural pattern is particularly handy in large scale decentralised systems aiming for resilience and low latency. Think of a central hub that is the single source of truth for data but it also has a lot of clients which mirror/store large portions of this data for some processing purposes. This could be patient records.

There’s no harm in holding local patient’s data inside the clinics hub. Just imagine if all the clinics in the country would have to read and write to a single server! In the clinics scenario, your local clinic would have to notify a central hub on any change in your record. This information would have to be then propagated to any other clinic that may be storing your records. This would happen via our event.

Here you can read a bit more about this pattern.

Wrapping up

Summing up, we have Event Carried State Transfer and Event Sourcing. Both are representing changes in domains but in different problem context. First of them aids decentralisation and resilience while the second allows data to “time travel”. Then we have our Domain or Notification events which promote decoupling of complex domains more than anything else. There is no harm in mixing and matching these patterns. As long as you are aware of the pros and cons. As long as there is a clear separation between each pattern. And all the developers understand which to use in particular scenarios.

Hope this article helped you understand events a little better. And as always please feel free to leave your thoughts in the comments.

Tags: