Event Chaining
Learn how to create sequences of related events using the chain mechanism. Event chaining allows you to track workflows and processes that span multiple steps across your application.
What You'll Learn
- How event chaining works
- How to create followup events
- How ChainRef links events together
- How ChainPosition tracks progress through a chain
- Practical use cases for event chaining
Prerequisites
- Go 1.25+ installed
- Completed the Quick Start guide
- Understand basic publish/subscribe (see Basic Pub/Sub)
- Familiar with the Chain, ChainRef, ChainPosition, and Followup concepts
How Event Chaining Works
In it-happened, events can be linked together in chains. A chain is a sequence of related events that share a common reference (ChainRef). Each event in the chain has a position (ChainPosition) that indicates its place in the sequence.
The first event in a chain has ChainPosition 0. When you create a followup event from it, the followup shares the same ChainRef but has ChainPosition 1, and so on.
Step 1: Create the Initial Event
Start by creating the first event in your chain:
This creates an event with a unique ID. Since it is the first event in the chain, the ChainRef is initialized with the event ID, and the ChainPosition is 0.
Step 2: Create a Followup Event
When handling an event, you can create a followup that continues the chain:
sub.On(event.Is("order.created"), func(e event.Event) {
// Create a followup event
followupEvt := e.NewFollowup(
OrderProcessedPayload{
OrderID: payload.OrderID,
Status: "processing",
},
)
bus.Publish(followupEvt)
})
The NewFollowup method creates a new event that:
- Shares the same ChainRef as the parent event
- Has ChainPosition incremented by 1
- Carries the new payload you provide
Step 3: Continue the Chain
You can continue creating followups to build longer chains:
sub.On(event.Is("order.processed"), func(e event.Event) {
completionEvt := e.NewFollowup(
OrderCompletedPayload{
OrderID: payload.OrderID,
Total: 100.0,
},
)
bus.Publish(completionEvt)
})
Step 4: Track Chain Progress
You can inspect the chain information on any event:
sub.On(event.IsOneOf("order.created", "order.processed", "order.completed"), func(e event.Event) {
fmt.Printf("ID: %s, ChainRef: %s, Position: %d\n",
e.ID(),
e.ChainRef(),
e.ChainPosition())
})
This will show that all events in the chain share the same ChainRef, but each has a unique ChainPosition and ID.
Complete Example
See the complete, runnable example:
📁 examples/event-chaining/main.go
To run it:
Expected output:
Starting order workflow for: <event-id>
Order created: ORD-12345 for $99.99
Order processed: ORD-12345 with status: processing
Order completed: ORD-12345 for total $100.00
Order workflow completed!
Key Concepts Used
- Chain: A sequence of related events (see Concepts)
- ChainRef: The unique identifier linking all events in a chain (see Concepts)
- ChainPosition: The position of an event within its chain (see Concepts)
- Followup: A new event created from a parent event in a chain (see Concepts)
Real-World Use Cases
Event chaining is useful for:
- Multi-step workflows: Track the progress of a business process through multiple stages
- Request-response patterns: Match responses to their original requests
- Saga pattern: Manage distributed transactions across multiple services
- Audit trails: Maintain a complete history of related actions
- State machines: Model state transitions as a series of events
Next Steps
- Basic Publish/Subscribe - Review the fundamentals
- Using Matchers - Learn advanced event filtering
- Using Carriers - Dispatch multiple events as a unit