3

Let's say we have a write model (domain) that generates two events:

  • CarrierAdded(...)
  • BusConnectionCreated(carrier, ...)

Carrier and BusConnection classes are (part of) separate aggregates. BusConnection is assigned to a Carrier and contains its CarrierId (separate aggregates are referenced only by id).

Everything is fine both in write model and read model during the normal flow of commands and events, but the problem appears when we want to rebuild/add new read model from scratch.

Many people suggests (e.g. akka-persistence library) that events are stored per aggregate in the event store. When the denormalizer asks for events to be replied he gets two independent stream of events from each of the aggregates. The problem is that some events from different aggregates like in the example above needs to be replied in the same order they were added to the event store. That means we need some kind of causal dependencies / partial ordering.

And finally my questions:

  • Should I rethink my design of the domain (bad aggregate boundaries?) or
  • Do I need only to enforce the partial ordering?

If the latter, what is the most efficient way to do that?

  • Global counters? Don't seem to be scalable.
  • Some kind of vector clocks?
  • Detect such problems in denormalizer when they appear? E.g. we got CarrierId, we don't have the CarrierAdded event with this id yet, so we stash the event and wait for the expected one first
  • Introduce some order in terms of processing the events in the replay mode? E.g. all events concerning Carriers first, BusConnection related events later?
Mequrel
  • 727
  • 6
  • 12

1 Answers1

6

No, IMO your design is perfectly fine and quite common. You have to enforce partial ordering somehow.

I'm not familiar with akka-persistence, but some event stores achieve partial ordering by recording the time stamp of the event and replay the events in the order of their timestamp. Some event stores indeed use global counters, for example when the underlying database is relational and the system does not need to be scaled out -- sequence numbers provided by the database are working fine here.

Timestamps of course only guarantee ordering up to a given granularity, often 1ms. In some cases, this is already enough, for example if you can ensure that CarrierAdded is never occurring within the same millisecond as BusCarrierAdded.

As for scalability, make sure scale-out is indeed an issue in your case, a bus transportation system does not seem to be "massive"... if you can live with a single master database server, sequence numbers of events in the event stream are a straight forward and reliable way to achieve the desired partial ordering. Sequence numbers may fail for "interweaving"/parallel commits, thus you need to make sure that the CarrierAdded event is always added to the event store/database before BusCarrierAdded, but this seems likely in your scenario.

If you do actually need scale-out and both events may appear in the same millisecond, you indeed have to detect the problem within the handler and use denormalization. This sounds harder than it is, as it can easily achieved with a very simple state machine. Jonathan Oliver has blogged about state matchines here in the context of sagas, but the principle applies here as well.

Alexander Langer
  • 2,883
  • 16
  • 18
  • For breaking close ties, what about an "causation counter" on events and commands? In essence, a way to gauge the "depth" of a command/event in the tree of side-effects, rooted to whatever external/schedule/user-trigger command that started the cascade at step zero. When you have two items with the same timestamp, it will usually be safe to process the one with the lower-counter first, even if they aren't actually related to one-another. – Darien May 22 '14 at 00:03
  • Yes, that could work, too (but I imagine it might be hard to implement or causes some burden for the developers). – Alexander Langer May 22 '14 at 07:08