domain

package
v1.0.0-beta.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 25, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// EventTypeCreate represents an entity creation event.
	EventTypeCreate = "created"

	// EventTypeUpdate represents an entity update event.
	EventTypeUpdate = "updated"

	// EventTypeDelete represents an entity deletion event.
	EventTypeDelete = "deleted"

	// EventTypeTriple represents an RDF-style relationship event (subject-predicate-object).
	EventTypeTriple = "triple"
)

Standard event type constants for common domain operations.

Variables

View Source
var (
	// ErrEventNotFound is returned when an event is not found in the store.
	ErrEventNotFound = errors.New("event not found")

	// ErrConcurrencyConflict is returned when there's a version conflict during event persistence.
	ErrConcurrencyConflict = errors.New("concurrency conflict: expected version mismatch")

	// ErrInvalidEvent is returned when an event is invalid or malformed.
	ErrInvalidEvent = errors.New("invalid event")

	// ErrGlobalOrderingNotSupported is returned by ReadAfter on stores that
	// cannot provide a global, cross-aggregate ordering of events.
	ErrGlobalOrderingNotSupported = errors.New("event store does not support a global ordered feed")
)

Functions

func EventTypeFor

func EventTypeFor(entityType, action string) string

EventTypeFor constructs an event type string from an entity type and action. For example, EventTypeFor("user", EventTypeCreate) returns "user.created".

func IsStandardEventType

func IsStandardEventType(eventType string) bool

IsStandardEventType checks if the given event type is one of the standard types.

func MarshalEventToJSON

func MarshalEventToJSON[T any](envelope EventEnvelope[T]) ([]byte, error)

MarshalEventToJSON marshals a generic EventEnvelope to JSON.

func RegisterType

func RegisterType[T any](d *EventDispatcher, eventType string, factory func() T) error

RegisterType registers a type factory for an event type to enable type-safe deserialization. This is separate from handler registration and is used when unmarshaling events from storage.

func Subscribe

func Subscribe[T any](d *EventDispatcher, eventType string, handler EventHandler[T]) error

Subscribe registers a typed event handler for a specific event type. The handler will be called when events of the specified type are dispatched. Multiple handlers can be registered for the same event type. This is a generic function (not a method) because Go doesn't support generic methods on non-generic types.

Types

type BasicTripleEvent

type BasicTripleEvent struct {
	Subject   string `json:"subject"`
	Predicate string `json:"predicate"`
	Object    string `json:"object"`
	Original  int64  `json:"original"`
}

BasicTripleEvent is a standard triple event payload that other strongly typed triple events can use in composition. It represents a subject-predicate-object relationship with an original identifier.

type Entity

type Entity interface {
	// GetID returns the aggregate ID of the entity.
	GetID() string

	// GetSequenceNo returns the last event sequence number from when the entity was hydrated.
	GetSequenceNo() int

	// GetUncommittedEvents returns all uncommitted events that have been recorded but not yet persisted.
	GetUncommittedEvents() []EventEnvelope[any]

	// ClearUncommittedEvents removes all uncommitted events, typically called after successful persistence.
	ClearUncommittedEvents()
}

Entity is an interface for entities that can be tracked by UnitOfWork. It provides methods to access entity state and uncommitted events.

type Event

type Event interface {
	GetID() string
	GetSequenceNo() int
}

Event is an optional interface that events can implement for convenience. It provides a way to extract the aggregate ID from an event.

type EventDispatcher

type EventDispatcher struct {
	// contains filtered or unexported fields
}

EventDispatcher is responsible for registering event handlers and dispatching events to them. It acts as both a handler registry and event dispatcher.

func NewEventDispatcher

func NewEventDispatcher() *EventDispatcher

NewEventDispatcher creates a new EventDispatcher instance.

func (*EventDispatcher) Dispatch

func (d *EventDispatcher) Dispatch(ctx context.Context, envelope EventEnvelope[any]) error

Dispatch dispatches an event to all registered handlers for the event type and matching patterns. Handlers are executed in parallel using goroutines. If any handler returns an error, it is collected and returned after all handlers complete. Pattern matching: "user.created" triggers handlers for "user.created", "user.*", "*.created", and "*.*"

func (*EventDispatcher) SubscribeWildcard

func (d *EventDispatcher) SubscribeWildcard(handler func(context.Context, EventEnvelope[any]) error) error

SubscribeWildcard registers a catch-all handler that will be called for all event types. Wildcard handlers are executed in parallel with pattern-matched handlers.

func (*EventDispatcher) UnmarshalEvent

func (d *EventDispatcher) UnmarshalEvent(ctx context.Context, data []byte, eventType string) (EventEnvelope[any], error)

UnmarshalEvent unmarshals an event from JSON using the registered type factory. It looks up the type factory from the registry and reconstructs EventEnvelope[any] with the typed payload.

type EventEnvelope

type EventEnvelope[T any] struct {
	ID            string                 `json:"id"`
	AggregateID   string                 `json:"aggregate_id"`
	EventType     string                 `json:"event_type"`
	Payload       T                      `json:"payload"`
	Created       time.Time              `json:"timestamp"`
	SequenceNo    int                    `json:"sequence_no"`
	TransactionID string                 `json:"transaction_id,omitempty"`
	Metadata      map[string]interface{} `json:"metadata,omitempty"`

	// Position is the event's place in the store's global, cross-aggregate
	// commit order. It is assigned by the event store at persistence time and
	// populated on reads; it is zero on envelopes that have not been persisted
	// (or that were stored before global ordering existed and have not been
	// backfilled). Positions are strictly increasing but not necessarily dense.
	Position int64 `json:"position,omitempty"`
}

EventEnvelope is a generic struct that wraps event payloads with metadata for transport and persistence. The type parameter T represents the strongly-typed event payload.

func NewEventEnvelope

func NewEventEnvelope[T any](payload T, aggregateID, eventType string, sequenceNo int) EventEnvelope[T]

NewEventEnvelope creates a new EventEnvelope with the given payload and metadata. If the payload implements the Event interface, the AggregateID is extracted from it. Otherwise, the provided aggregateID parameter is used. sequenceNo is the sequence number for this event within the aggregate's event stream.

func ToAnyEnvelope

func ToAnyEnvelope[T any](envelope EventEnvelope[T]) EventEnvelope[any]

ToAnyEnvelope converts an EventEnvelope[T] to EventEnvelope[any] for storage. This allows storing events with different payload types together in the event store.

func UnmarshalEventFromJSON

func UnmarshalEventFromJSON[T any](data []byte) (EventEnvelope[T], error)

UnmarshalEventFromJSON unmarshals an EventEnvelope from JSON. The type parameter T must match the payload type in the JSON data.

func WrapEvent

func WrapEvent[T any](payload T, aggregateID, eventType string, sequenceNo int) (EventEnvelope[T], error)

WrapEvent wraps a typed payload in a generic EventEnvelope. If the payload implements the Event interface, the AggregateID is extracted from it. Otherwise, the provided aggregateID parameter is used. sequenceNo is the sequence number for this event within the aggregate's event stream.

func (*EventEnvelope[T]) MarshalJSON

func (e *EventEnvelope[T]) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler for EventEnvelope. This custom implementation ensures the generic type is properly serialized.

func (*EventEnvelope[T]) UnmarshalJSON

func (e *EventEnvelope[T]) UnmarshalJSON(data []byte) error

UnmarshalJSON implements json.Unmarshaler for EventEnvelope. This custom implementation ensures the generic type is properly deserialized.

type EventHandler

type EventHandler[T any] func(ctx context.Context, env EventEnvelope[T]) error

EventHandler is a type-safe handler function for processing events. The type parameter T represents the strongly-typed event payload.

type EventStore

type EventStore interface {
	// Append appends one or more events to the event store for a given aggregate.
	// It returns an error if the expected version doesn't match (optimistic concurrency control).
	// If expectedVersion is -1, no version check is performed.
	Append(ctx context.Context, aggregateID string, expectedVersion int, events ...EventEnvelope[any]) error

	// GetEvents retrieves all events for a given aggregate ID.
	// Returns an empty slice if no events are found.
	GetEvents(ctx context.Context, aggregateID string) ([]EventEnvelope[any], error)

	// GetEventsFromVersion retrieves events for an aggregate starting from a specific version.
	// Returns an empty slice if no events are found.
	GetEventsFromVersion(ctx context.Context, aggregateID string, fromVersion int) ([]EventEnvelope[any], error)

	// GetEventsRange retrieves events for an aggregate within a version range.
	// If fromVersion is -1, it defaults to version 1 (the first event).
	// If toVersion is -1, it retrieves all events from fromVersion to the end.
	// Returns an empty slice if no events are found in the range.
	GetEventsRange(ctx context.Context, aggregateID string, fromVersion, toVersion int) ([]EventEnvelope[any], error)

	// GetEventByID retrieves a specific event by its ID.
	// Returns ErrEventNotFound if the event doesn't exist.
	GetEventByID(ctx context.Context, eventID string) (EventEnvelope[any], error)

	// GetEventsByTransactionID retrieves all events that share the given transaction ID,
	// ordered by aggregate ID then sequence number.
	// transactionID must not be empty; implementations return ErrInvalidEvent if it is.
	// Returns an empty slice if no events are found.
	GetEventsByTransactionID(ctx context.Context, transactionID string) ([]EventEnvelope[any], error)

	// GetCurrentVersion returns the current version number for an aggregate.
	// Returns 0 if the aggregate doesn't exist.
	GetCurrentVersion(ctx context.Context, aggregateID string) (int, error)

	// ReadAfter returns committed events across all aggregates whose global
	// Position is greater than afterPosition, ordered by Position ascending.
	// At most limit events are returned; limit <= 0 means no limit.
	//
	// The result is safe to use as a resumable feed: once an event is
	// returned, no event with a smaller Position will ever appear in a later
	// call. Implementations backed by databases with concurrent writers must
	// withhold events whose positions are visible before an earlier-position
	// transaction has committed. The cost of that guarantee is liveness, not
	// correctness: a long-running write transaction anywhere in the database
	// delays the feed (an empty result can mean "caught up" or "withheld
	// behind an in-flight writer").
	//
	// Stores without a global ordering return ErrGlobalOrderingNotSupported.
	ReadAfter(ctx context.Context, afterPosition int64, limit int) ([]EventEnvelope[any], error)

	// HeadPosition returns the highest Position that ReadAfter could
	// currently deliver (0 when the store is empty). Feed consumers use it to
	// measure lag against their checkpoint.
	//
	// Stores without a global ordering return ErrGlobalOrderingNotSupported.
	HeadPosition(ctx context.Context) (int64, error)

	// Close closes the event store and releases any resources.
	Close() error
}

EventStore defines the interface for persisting and retrieving events. Implementations should be thread-safe and handle concurrent access. Events are stored as EventEnvelope[any] to allow storing events with different payload types together.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL