Search code examples
eventsdesign-patternsevent-sourcing

Event Sourcing: Can I describe the parent object state via its member object events?


I am trying to implement event sourcing for an object, which is a parent of multiple other objects. Like so:

Object A0
├── sub-object array
│   ├── Object B0
│   └── Object B1
└── Object C0

The events that describe Object A0 are a mixture of the events generated when creating Object A0 (such as connecting it to Object B0, B1 and C0) and the Events that make up Objects B0, B1 and C0 themselves. Object B0, B1 and C0 are event sourced and always come into existence after Object A0.

My approach would be to aggregate all events of the objects to recreate the state of Object 0. This means, that the events to recreate object 0 are spread across multiple collections (events-A, events-B, events-C) in one database. This means I am fetching the events for Object A0, then when the events hints to it B0, B1 and then the events for C0. Are there disadvantages or hidden pitfalls to that approach?

I am trying to abide by the single-source-of-truth principle. However that can have multiple interpretations in this scenario. Either it means creating only one event per state-change or alternatively all events should be possible to be fetched from one simple query/attached to Object A0.
For the latter it would mean I could have events such as sub-object state changed. However that would duplicate the information.


Solution

  • As a rule: all information that participates in a single logical transaction should be stored in the same "event stream", regardless of how many distinct instances of the entity design pattern appear in your implementation when modifying data.

    So in your example, we'd normally have a single stream of events, not one stream per entity. The rationale here is that having a single stream makes persistence failure modes easier to reason about (how would you recover if streams for A0 and B1 were updated, but streams for B0 and C0 failed?) With a single event stream, we can be certain that the persisted representation of the domain entities always conform to the invariant that the domain model is intended to maintain.

    (If you happen to be using event storage that allows you to modify multiple streams in a single all-or-nothing transaction, then you can consider alternative designs. But if you intend your solution to be "agnostic" with regards to event storage, then there's some risk in assuming that capability will be available. And of course if your event storage is distributed then the costs of resilience go up.)

    Having a single stream of events doesn't necessarily mean that information is duplicated across multiple events, but it may mean that the same branching logic appears in two different places (A0 branches to forward event X to C0, whereas C0 branches to its internal logic for copying event X data into its transient data structures).