When Juliet asked Romeo “What’s in a Name?” she was trying to wish away family history—the inheritance, customs, and all the feuding baggage that came with being a Capulet. A name is an arbitrary, agreed-upon label, but it often points to a lineage of events.
To Juliet's credit, being able to let go of systemic baggage allows us to build a new future (and new software without the cruft of the old). But, just as she and Romeo learned, the conventions underpinning two different domains may mean they can't function together without additional work being done. Context is still king.
So, if cooler heads reign when talking about the state of events, what's to be done to help systems evolve and adapt to change? How do we build scalable, interoperable event-driven systems? How do we keep inertia and the cost of change down?
A common syntax between systems and events and behaviors becomes necessary. In this, the Semantic Web can help. And thus the CNCF CloudEvent spec was born. This relatively simple standard goes a long way toward facilitating event conversations between systems. Using open standards can help you scale.
------------------ Message -------------------
Concept Name: identity.user.domain-events
------------------- key ----------------------
Key: 17cce427-2027-4b54-9149-626df8109d00
------------------ headers -------------------
content-type: application/cloudevents+json; charset=UTF-8
------------------- value --------------------
{
"specversion" : "1.0",
"type" : "usersignedin",
"source" : "identity.user",
"id" : "b65d1b31-1f46-4fbe-a7b4-e93f8c96a449", //event uuid
"time" : "2018-04-05T03:56:24Z",
"datacontenttype" : "application/json",
“subject” : “17cce427-2027-4b54-9149-626df8109d00”, //source key
“traceparent”: “60080489-f508-4dfd-a96a-62cb8a382689”, //correlation key
"data" : {
... serialized JSON text ...
}
}
CNCF Structured Content Mode Example
For systems to interact they must have a shared language, roles, and conventions that allow them to work together (sidenote: this is the basis of “culture” in any system). Some conventions make eventing easier and less costly.
Time
or StartedAt
and EndedAt
)When building new systems, we often act as well-meaning Juliets trying to share information across systems and break down old and unknown conventions to do so. Sometimes we get things mixed up. We’re human. It happens. We can iterate into being better.
Are you trying to wish away conventions or mutate definitions? Immutability is important—if you’re tempted to edit a past event, you’re likely doing it wrong, and now may be a good time to rethink your system design.
What should be included in an event? Atomicity helps us understand what does and doesn’t belong. Don’t include events inside events—define a new aggregate or entity instead. This can be helpful when modeling or modifying your system.
Events must be accurately ordered in time by the publishing system—make sure the clocks are synchronized and logging correctly. Make sure you can sort, partition, index, and filter. In the systems I work on we’ve standardized around ids, keys, time and date names, and formats.
Events are different from commands. In computing, if they haven't happened yet they aren't an event. People often confuse commands with events or event messages. Models can become conflated. Event-driven isn’t the same thing as event-sourced. Conflation contributes to unclear responsibilities, looping, or even deadlocked systems over time.
As you build, it’s important to document the conventions and constraints. Logic can be automated with certain assumptions taken as truth over time.
These could potentially help you build out your logical base:
Since events express key pieces of a lifecycle this is something that can help you understand and code your domain.
Here’s an example of events and commands for a Meeting entity in the Meetings domain. Notice how some events are aggregated into a single domain event.
DOMAIN Meetings
EXTERNAL COMMAND GetScheduledMeetings()
EXTERNAL COMMAND ScheduleMeeting(Type, DateTime, Users)
INTERNAL COMMAND AddUsersToScheduledMeeting(ID, Users)
DOMAIN EVENT MeetingScheduled
EXTERNAL COMMAND AddUsersToMeeting(ID, Users)
INTERNAL EVENT UsersAddedToMeeting
DOMAIN EVENT MeetingAdjusted
INTERNAL EVENT DailyMeetingsJobRan
INTERNAL COMMAND StartMeeting()
DOMAIN EVENT MeetingOccurred
INTERNAL COMMAND GetTodaysMeetings()
INTERNAL EVENT DailyMeetingsReportRan
DOMAIN EVENT MeetingsCompacted
INTERNAL COMMAND DeleteMeeting()
DOMAIN EVENT MeetingTombstoned
Your system of record might only capture and publish the DOMAIN EVENTS in the example above. Like zooming in on a map, the frame, or level of abstraction, matters for what messages you choose to publish to your consumers.
As your system becomes more complex, structural evolution (of which versioning is part) of the domain may become important to record in your event store. For that to happen, these changes must be included in the lifecycle record. Like genetic code, adaptation may need to be codified and observed to be propagated in a complex system.
SYSTEM COMMAND RegisterDomain(Meetings)
SYSTEM EVENT system.DomainRegistered
SYSTEM COMMAND AddTopic(Meetings, root, v1.0)
SYSTEM EVENT system.TopicAdded
SYSTEM EVENT system.TopicSchemaRegistered
DOMAIN Meetings
EXTERNAL COMMAND GetScheduledMeetings()
EXTERNAL COMMAND ScheduleMeeting(Type, DateTime, Users)
DOMAIN EVENT root.MeetingScheduled
EXTERNAL COMMAND AddUsersToMeeting(ID, Users)
INTERNAL EVENT UsersAddedToMeeting
DOMAIN EVENT root.MeetingAdjusted
INTERNAL EVENT DailyMeetingsJobRan
INTERNAL COMMAND StartMeeting()
DOMAIN EVENT root.MeetingOccurred
INTERNAL COMMAND GetTodaysMeetings()
INTERNAL EVENT DailyMeetingsReportRan
DOMAIN EVENT root.DailyMeetingsCompacted
INTERNAL COMMAND DeleteMeeting()
DOMAIN EVENT root.MeetingTombstoned
SYSTEM COMMAND VersionTopic(Meetings, root, v2.0)
SYSTEM EVENT system.TopicRegistered
SYSTEM EVENT system.TopicSchemaRegistered
SYSTEM EVENT system.TopicVersioned
Make sure your lifecycle works on paper. You can mock up the stories you want to tell. If your mockup covers all of your use cases or BDD scenarios for your MVP you’ve got a hella good start on your system.
Sometimes events carry state and relations. Sometimes they don’t. They can act an awful lot like Montagues and Capulets. Work to understand the roles and teams that own entities. Find overlap and areas where conventions or standards might help the business communicate. Be aware of relational dependencies. Try not to act as a pass-through for data that is owned by other teams' models. It makes change difficult later on.
In order to integrate, to communicate and to interoperate events must convey a shared set of meaning within both human and automated systems. Name your events carefully and well. Build systems that can adapt and evolve with a reasonable amount of shared intention. Communicate regularly with your consumers.
We build stories with events; we use them to semantically and syntactically plot, build, communicate, and collaborate.
Take some lessons from Shakespeare’s tragedy. You can create common language and conventions that allow you to inform people about systemic or breaking change. This can help keep the cost of change down. Seek awareness, use descriptive naming standards, register schemas, and share practices around persistent events.
Additional Resources