Steven
Steven

Reputation: 879

Best practice for naming Event Types in Event Sourcing

When building an event store, the typical approach is to serialize the event and then persist the type of the event, the body of the event (the serialized event itself), an identifier and the time it occurred.

When it comes to the event type, are there any best practises as to how these should be stored and referenced? Examples I see store the fully qualified path of the class ie.

com.company.project.package.XXXXEvent

What effort is then required though if you decide to refactor your project structure?

Upvotes: 4

Views: 2726

Answers (4)

Stephan Eggermont
Stephan Eggermont

Reputation: 15907

If you store events from multiple bounded contexts in one store, BoundedContext.EventThatHappened. Past tense for events, and event names are unique for a bounded context. As your events will change implementation, there is no direct connection to a class name.

Upvotes: 2

Mattias Holmqvist
Mattias Holmqvist

Reputation: 832

Since event sourcing is about storing Domain Events, we prefer to avoid package-names or other technical properties in the events. Especially when it comes to naming them, since the name should be part of ubiquitous language. Domain Experts and other people don't lean on package names when making conversation about the domain. Package names are a language construct that also ties the storage of the Domain Events with the use of them within your software, which is another reason to avoid this solution.

We sometimes use the short class name (such as Class.forName in Java) to make mapping to code simpler and more automatic, but the class names should in that case be carefully chosen to match the ubiquitous language so that it still is not too implementation-specific.

Additionally, adding a prefix opens upp the possibility to have multiple Event Types with the same name but using different prefixes. Domain Events are part of the context of the Aggregate they are emitted from and therefore the Aggregate type can be useful to embed in the event. It will scope your events so you don't have to make up synthetic prefixes.

Upvotes: 3

VoiceOfUnreason
VoiceOfUnreason

Reputation: 57239

What effort is then required though if you decide to refactor your project structure?

Not a lot of effort, but some discipline.

Events are messages, and long term viability of messages depend on having a schema, where the schema is deliberately designed to support forward and backward compatibility.

So something like "event type" would be a field name that can be any of an open set of identifiers which would each have an official spelling and semantics.

The spelling conventions that you use don't matter - you can use something that looks like a name in a hierarchical namespace, or you can use a URI, or even just a number like a surrogate key.

The identifiers, whatever convention you use, are coupled to the specification -- not to the class hierarchy that implements them.

In other words, there's no particular reason that org.example.events.Stopped necessarily implies the existence of a type org.example.events.Stopped.

Your "factories" are supposed to create instances of the correct classes/data structures from the messages, and while the naive mapping from schema identifier to class identifier works, then yes, they can take that shortcut. But when you decide to refactor your packages, you have to change the implementation of the factory such that the old identifiers from the message schema map to the new class implementations.

In other words, using something like Class.forName is a shortcut, which you abandon in favor of doing the translation explicitly when the short cut no longer works.

Upvotes: 4

Alexey Zimarev
Alexey Zimarev

Reputation: 19610

After years running event-sourced applications in production, we avoid using fully qualified class names or any other platform-specific identifiers for event types.

An event type is just a string that should allow any kind of reader to understand how the event should be deserialized. You are also absolutely right about the issue with refactoring the application structure that might lead to changes in the class name.

Therefore, we use a pre-configured map that allows resolving the object type to a string and to reverse the string to an event type. By doing so, we detach the event type meta from the actual class and get the freedom to read and write events using different languages and stacks, also being able to freely move classes around of needed.

Upvotes: 8

Related Questions