Patterns for Decoupling in Distributed Systems: Completeness Guarantee

Design the set of Domain Events from a producer so that they can be used to rebuild the producer's state.

By Mathias Verraes
Published on 08 May 2019



Completeness Guarantee

Design the set of Domain Events from a producer so that they can be used to rebuild the producer’s state.

Problem

Often, the events emitted by a producer, are designed haphazardly. New event types are added whenever a new feature requires them. A consumer needs to be aware of an event, so we come up with an event in the producer, give it a name, and add some attributes the consumer needs. Then perhaps later another consumer is added, and it reuses some existing events, but it needs just a little bit of additional information. This is added as new attributes in an existing event. Later, a consumer is removed, but the events still exist.

After a while it becomes hard to understand which events to use where, what they contain, and what they mean exactly. Consumers become indirectly coupled to each other, and the producer needs to support backwards compatibility to a growing sets of messy events.

Solution

We need an organizing principle that answers the question “What goes in an event?”, looking holistically at the set of events from a producer.

A Completeness Guarantee is the idea that if a consumer listens to the entire stream of Domain Events, it must be able to faithfully reproduce the entire state of the producer. Every action in the producer that changes state, causes an event to be emitted, and each event contains every unit of information that is affected by the state change. On top of that, ideally the events contain no redundant information: the event only has the attributes that have changed, and not a single one more. (Note: Fat Events discusses cases where you’d want to add redundant attributes.)

The effect of a Completeness Guarantee is that it becomes clear and obvious which events are relevant, what they mean, what attributes they contain, etc. There is the certainty that any feature that could be implemented in the producer, can be implemented in a consumer, with no missing information. This enables a system designer to choose where to move certain responsibilities.

The Completeness Guarantee gives a great degree of decoupling to consumers: they never need to query the producer, as they already have access to all information.

Implementation

In systems that use Event Sourcing, you get the Completeness Guarantee for free. I define Event Sourcing as a persistence style where a store of domain events is the single source of truth for the system’s state. State may stored in other ways, but this state is discardable, and can be rebuilt from the event store. If the events are the single source of truth, it follows that all state can be rebuild, so the guarantee is sound.

For system designers used to Event Sourcing, the Completeness Guarantee comes naturally. The point of the pattern is to show that it is useful outside of Event Sourcing as well.

How do you actually achieve that guarantee, when the single source of truth of a system is a mode traditional database, which stores state instead of events? One approach is to write a test for it. This test listens to all events from production, and projects them into an identical schema as the producer’s real schema. The test compares to projected state to the production state, and alerts when there are differences. However, instead of going through the effort of building and running this test, you might be better off just using Event Sourcing in the first place.