Documentation
¶
Overview ¶
Package journal provides context-based event journaling.
A Handler — the user-provided persistence layer — is attached to a context with Setup. Events are then recorded with Add, which threads a lastID through the context so each event links to the one before it, forming an ordered, append-only chain.
Journaling is an optional aspect of the application flow, never a mandatory step: Add is a silent no-op when Setup was not called, when the context is cancelled, or when the handler reports failure. It never surfaces an error to the caller, so it cannot break business logic. All state lives in the context; the package holds no global state.
Example ¶
package main
import (
"context"
"fmt"
"time"
"github.com/go-devkit/journal"
)
// recorder is a minimal Handler standing in for a real persistence layer. It
// assigns sequential IDs and remembers the lastID it was handed.
type recorder struct {
n int
lastSeen string
}
func (h *recorder) Add(_ context.Context, lastID string, _ string, _ time.Time, _ map[string]any) string {
h.n++
h.lastSeen = lastID
return fmt.Sprintf("evt_%d", h.n)
}
func main() {
ctx := journal.Setup(context.Background(), &recorder{})
ctx = journal.Add(ctx, "order.created", map[string]any{"total": 99.95})
ctx = journal.Add(ctx, "order.payment.charged", nil)
id, _ := journal.LastID(ctx)
fmt.Println(id)
}
Output: evt_2
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Add ¶
Add records an event. Journaling is an optional aspect of the application flow, never a mandatory step: it is a silent no-op if Setup was not called or if the context is already cancelled, and the handler owns its own error handling. This keeps call sites clean and ensures journaling never affects application correctness or performance.
func LastID ¶
LastID returns the most recent event ID from the context. The boolean indicates whether an ID is present.
Example ¶
package main
import (
"context"
"fmt"
"time"
"github.com/go-devkit/journal"
)
// recorder is a minimal Handler standing in for a real persistence layer. It
// assigns sequential IDs and remembers the lastID it was handed.
type recorder struct {
n int
lastSeen string
}
func (h *recorder) Add(_ context.Context, lastID string, _ string, _ time.Time, _ map[string]any) string {
h.n++
h.lastSeen = lastID
return fmt.Sprintf("evt_%d", h.n)
}
func main() {
ctx := journal.Setup(context.Background(), &recorder{})
ctx = journal.Add(ctx, "order.created", nil)
id, ok := journal.LastID(ctx)
fmt.Println(id, ok)
}
Output: evt_1 true
func WithLastID ¶
WithLastID seeds the context with an event ID so that the next call to Add chains from it. The event chain is stateless — it lives entirely in the context, so setting this does not interfere with Setup or any prior state. This is useful when continuing an event chain across system boundaries, e.g. after receiving an event ID from another service via NATS or HTTP.
An empty id is a no-op: seeding one would masquerade as a root event and silently break the chain, so the context is returned unchanged.
Example ¶
package main
import (
"context"
"fmt"
"time"
"github.com/go-devkit/journal"
)
// recorder is a minimal Handler standing in for a real persistence layer. It
// assigns sequential IDs and remembers the lastID it was handed.
type recorder struct {
n int
lastSeen string
}
func (h *recorder) Add(_ context.Context, lastID string, _ string, _ time.Time, _ map[string]any) string {
h.n++
h.lastSeen = lastID
return fmt.Sprintf("evt_%d", h.n)
}
func main() {
h := &recorder{}
ctx := journal.Setup(context.Background(), h)
// Continue a chain started elsewhere, e.g. an ID received from another service.
ctx = journal.WithLastID(ctx, "evt_from_upstream")
journal.Add(ctx, "order.received", nil)
fmt.Println(h.lastSeen)
}
Output: evt_from_upstream
Types ¶
type Handler ¶
type Handler interface {
Add(ctx context.Context, lastID string, key string, timestamp time.Time, data map[string]any) string
}
Handler is the user-provided persistence layer. Implementations own their own error handling: journaling is optional and must never surface a failure to the caller. On success, Add returns the new event's ID, used as lastID for the next event in the chain. On failure, return an empty string — the chain is preserved and the failed event is skipped.