events

package module
v1.13.5 Latest Latest
Warning

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

Go to latest
Published: Nov 6, 2026 License: Apache-2.0 Imports: 21 Imported by: 0

README

Forge Events, an event bus and event store for Go with in-memory, NATS and Redis brokers

Forge Events: event bus and event sourcing for Go

Forge Events is the event-driven architecture extension for Forge, the Go backend framework. It gives your app a publish and subscribe event bus, an append-only event store for event sourcing with snapshots, and pluggable message brokers: in-memory, NATS, Redis Pub/Sub and Redis Streams with consumer groups and a dead-letter stream. You publish domain events such as user.created from your handlers, and subscribers in the same process or in other services react to them.

Go CI Go Reference Apache 2.0

Documentation · Quick start · Features · Publishing · Handlers · Brokers · Event store · Configuration · FAQ

Quick start

You need Go 1.26 or later. The defaults use an in-memory broker and store, so there's nothing else to run.

go get github.com/xraph/forge@latest
go get github.com/xraph/forge/extensions/events@latest

This app publishes a user.created event from POST /users, and a named handler picks it up:

package main

import (
	"context"
	"log"
	"net/http"

	"github.com/xraph/forge"
	"github.com/xraph/forge/extensions/events"
	"github.com/xraph/forge/extensions/events/core"
)

type signup struct {
	Email string `json:"email"`
}

func main() {
	app := forge.NewApp(forge.AppConfig{
		Name:        "accounts",
		HTTPAddress: ":8080",
		Extensions: []forge.Extension{
			events.NewExtension(), // in-memory broker and store
		},
	})

	// Handle user.created once the bus is running.
	err := app.RegisterHookFn(forge.PhaseAfterStart, "welcome-email",
		func(ctx context.Context, a forge.App) error {
			bus, err := events.GetEventBus(a.Container())
			if err != nil {
				return err
			}

			welcome := events.NewTypedEventHandler("welcome-email", []string{"user.created"},
				func(ctx context.Context, e *core.Event) error {
					log.Printf("send welcome email to %v (user %s)", e.Data, e.AggregateID)

					return nil
				})

			return bus.Subscribe("user.created", welcome)
		})
	if err != nil {
		log.Fatal(err)
	}

	err = app.Router().POST("/users", func(ctx forge.Context) error {
		var in signup
		if err := ctx.BindJSON(&in); err != nil {
			return err
		}

		bus, err := events.GetEventBus(app.Container())
		if err != nil {
			return err
		}

		event := core.NewEvent("user.created", "user-1", in).WithSource("accounts-api")
		if err := bus.Publish(ctx.Context(), event); err != nil {
			return err
		}

		return ctx.JSON(http.StatusCreated, map[string]string{"event_id": event.ID})
	})
	if err != nil {
		log.Fatal(err)
	}

	if err := app.Run(); err != nil {
		log.Fatal(err)
	}
}

Run it and create a user:

go mod tidy
go run .

# in another terminal
curl -X POST localhost:8080/users -H 'Content-Type: application/json' -d '{"email":"ada@example.com"}'

The response carries the event ID, and the app logs send welcome email to {ada@example.com} (user user-1). The event is also in the store now, so GetEventsByAggregate(ctx, "user-1", 0) returns it.

Features

You need Forge Events gives you
Decoupled modules in one service An in-process event bus with named handlers per event type
Events between services NATS and Redis Pub/Sub brokers, selected by name
At-least-once delivery that survives restarts Redis Streams with consumer groups, acknowledgement after your handler succeeds, retries and a dead-letter stream
Event sourcing An event store with per-aggregate history, time-range queries, soft deletes and snapshots
A durable event log Any core.EventStore you lend the service with WithEventStore
Handler retries RetryPolicy with exponential backoff, a delay cap and your own retry rule
Cross-cutting concerns Handler middleware for logging, metrics and validation
Correlation across services CorrelationID, CausationID, Source and free-form metadata on every event

The extension registers four things in the container. Resolve them with the helpers or with forge.Inject:

Service Type Alias Helper
Event service *events.EventService events events.GetEventService(c)
Event bus core.EventBus eventBus events.GetEventBus(c)
Event store core.EventStore eventStore events.GetEventStore(c)
Handler registry *core.HandlerRegistry eventHandlerRegistry events.GetHandlerRegistry(c)

Each helper has a Must... twin that panics, and GetEventServiceFromApp(app) saves you the Container() call. The extension's Start opens the store, connects every enabled broker and starts the bus, so if a broker can't be reached, the app doesn't start.

Publish events

An event has a type, the ID of the aggregate it belongs to, and any data you want to carry:

event := core.NewEvent("order.paid", order.ID, order).
	WithVersion(2).
	WithSource("billing").
	WithCorrelationID(requestID).
	WithMetadata("tenant", tenantID)

err := bus.Publish(ctx, event)

NewEvent fills in a UUID, a UTC timestamp and version 1. The event type doubles as the topic name on every broker.

Publish saves the event to the store first, then sends it to the default broker. If you leave default_broker empty, it sends to every registered broker instead. If any destination fails, you get an error, and a retry can deliver again to brokers that already succeeded.

PublishTo(ctx, "nats", event) sends to one named broker without saving. Use it to relay an event that's already in your store, for example from an outbox, so you don't write it twice.

Subscribe handlers

bus.Subscribe(eventType, handler) subscribes a handler on every registered broker. A handler is anything with Handle, CanHandle and Name methods, and the package gives you a few ready-made ones:

Constructor What it is for
events.NewTypedEventHandler(name, types, fn) A named handler for one or more event types, with middleware and retries
events.NewDomainEventHandler(name, aggregateType, types, fn) A typed handler that only accepts events whose aggregate_type metadata matches
events.NewReflectionEventHandler(name, target) Calls Handle<Type>(ctx, event) on target, so it only fits event types that are valid Go identifiers, such as UserCreated
core.EventHandlerFunc(fn) A bare function. It subscribes anonymously, so you can't unsubscribe it by name and Redis Streams rejects it

Give your handlers stable names. bus.Unsubscribe(eventType, name) removes a handler by name, and Redis Streams uses the name to build the consumer group, so the name is what lets a restarted service pick up where it stopped.

On the in-memory broker every matching handler runs in its own goroutine with a 30 second timeout. A handler error is logged and counted, and the event isn't redelivered, so put retries on the handler itself:

handler := events.NewTypedEventHandler("charge-card", []string{"order.placed"}, chargeCard).
	WithRetryPolicy(events.NewRetryPolicy(3, 200*time.Millisecond).WithMaxDelay(5*time.Second)).
	WithMiddleware(events.LoggingMiddleware(logger), events.ValidationMiddleware())

NewRetryPolicy(maxRetries, initialDelay) doubles the delay on every attempt up to five minutes unless you cap it. By default it retries any error except context cancellation and deadline errors. WithRetryableErrors and WithShouldRetryFunc let you narrow that down.

Brokers

You list brokers under brokers, each with a name, a type and its own config map. The bus subscribes handlers on all of them and publishes to the default one.

Type Delivery config keys
memory In-process, lost on restart none
nats NATS core subjects, at most once url, name, username, password, token, max_reconnects, enable_compression
redis Redis Pub/Sub, at most once address or addresses, username, password, database, pool_size, master_name (Sentinel)
redis with enable_streams: true Redis Streams, at least once the Redis keys plus consumer_group, consumer_name, stream_max_len, stream_max_deliveries, stream_claim_idle, stream_poll_interval, stream_lease_duration, stream_ordering

The NATS broker defaults to nats://localhost:4222, and Redis defaults to localhost:6379. With more than one Redis address and no master_name, the broker uses a cluster client for Pub/Sub. Streams mode refuses Redis Cluster and needs a single server or Sentinel.

With Redis Streams, your handler acknowledges a message by returning nil. An error leaves it pending, and another attempt comes after stream_claim_idle. After stream_max_deliveries attempts (5 by default), the message moves to the <topic>:forge:dead-letter stream with its last error. Before you wire a handler that depends on that guarantee, check the broker with core.RequireDurableBroker:

broker, err := bus.GetBroker("orders")
if err != nil {
	return err
}

durable, err := core.RequireDurableBroker(broker)
if err != nil {
	return err // memory, NATS and Redis Pub/Sub return core.ErrDurableDeliveryUnavailable
}

return durable.SubscribeDurable(ctx, "order.placed", handler)

Retention limits, recovery after a deleted stream and replica fencing are in the Redis delivery modes guide.

Event store

Publish writes every event to the store before it goes out, so the store doubles as your event log. The built-in store keeps everything in memory, which is fine for development and tests and gone on restart.

For a durable log, implement core.EventStore over your database and lend it to the service:

ext := events.NewExtensionWithConfig(events.DefaultConfig(), events.WithEventStore(myStore))

A store you pass with WithEventStore is borrowed: the service checks its health at startup and never closes it. If you'd rather the service own the store and build a fresh one on every start, pass WithEventStoreFactory(func(ctx context.Context, cfg events.StoreConfig) (core.EventStore, error) {...}) instead. The stores package also contains PostgreSQL and MongoDB stores, but neither implements the full core.EventStore interface yet, so you can't hand them to the service as they stand.

Reading history back for event sourcing:

store, err := events.GetEventStore(app.Container())

history, err := store.GetEventsByAggregate(ctx, "order-123", 0)
last, err := store.GetLastEvent(ctx, "order-123")
paid, err := store.GetEventsByType(ctx, "order.paid", 100, 0)

err = store.CreateSnapshot(ctx, events.NewSnapshot("order-123", "order", state, last.Version))
snap, err := store.GetSnapshot(ctx, "order-123")

There's also GetEvents with an EventCriteria (types, aggregates, sources, time range, paging and sort), GetEventsSince, GetEventsInRange, per-type counts and soft deletes.

Configuration

The extension reads extensions.events from your config file over the defaults, so you only write what you change:

extensions:
  events:
    bus:
      default_broker: orders
    brokers:
      - name: orders
        type: redis
        enabled: true
        config:
          addresses: ["localhost:6379"]
          enable_streams: true
          consumer_group: billing
      - name: local
        type: memory
        enabled: true

When you replace the broker list, set bus.default_broker to one of your names as well. The default is memory, and startup fails if the default broker isn't enabled. You also need at least one enabled broker, unique names, and a type of memory, nats or redis.

Key Default What it does
bus.default_broker memory Where Publish sends. Empty means every broker
bus.enable_metrics true Records the two messaging duration histograms. See Tracing and metrics
bus.enable_tracing true Starts a producer span per broker publish and a consumer span per handler run
store.type memory memory is built in. Anything else needs WithEventStore or WithEventStoreFactory
store.database, store.table empty, events Passed to a store factory through StoreConfig
brokers[].name, type, enabled, config one memory broker The brokers to connect

bus.max_retries, bus.retry_delay, bus.processing_timeout, bus.buffer_size, bus.worker_count, brokers[].priority and the metrics section exist on the config structs, but the bus doesn't act on them today. Retries come from a handler's RetryPolicy (or from Redis Streams redelivery).

The bus has no worker pool, so bus.buffer_size and bus.worker_count are deprecated. Setting them still loads, and they do nothing. GetStats no longer returns workers or workers_count.

In code, build an events.Config (start from events.DefaultConfig()) and pass it to events.NewExtensionWithConfig(cfg, opts...). That's also where the store options go. A config file section still binds over what you pass.

Tracing and metrics

Tracing is on by default. Every broker a publish reaches gets its own producer span, named publish <event type>, and every handler run happens inside a consumer span named process <event type>. The consumer span is a child of the producer span, so one trace follows an event from the Publish call to each handler, across brokers and services. Handler errors and panics mark the consumer span as failed.

The producer puts the trace context into the event's Metadata: traceparent, tracestate when there is one, and baggage when the context has any. Your *core.Event and its map are never modified: the broker gets a copy. The values are plain strings, so they survive the JSON hop through NATS and Redis. An event from an older publisher, with no traceparent or a malformed one, starts a fresh trace instead of failing.

messaging.system is the broker's name, so two brokers show up as two systems.

With bus.enable_metrics on, the same spans feed two histograms, both in seconds:

  • messaging_client_operation_duration_seconds for publishes
  • messaging_process_duration_seconds for handler runs

Filter them by messaging_system to get one broker's numbers. Both carry messaging_destination_name (the event type), messaging_operation_name, messaging_operation_type and error_type.

If you register your own core.MessageBroker, it receives the bus's tracing wrapper in Subscribe, not the handler value you passed. The wrapper forwards Name, CanHandle and LogicalName, so identity checks keep working.

To turn spans off, set bus.enable_tracing: false. Trace context still passes through the event, so a service that doesn't trace doesn't break the trace of the services around it.

Series that went away

These had their own timers and counters, and the histograms above replace them. The scrape names carry the forge_ prefix twice, once from the app and once from the metric name:

Removed Use instead
forge_forge_events_publish_duration messaging_client_operation_duration_seconds
forge_forge_events_published_total, forge_forge_events_publish_success the _count of messaging_client_operation_duration_seconds with error_type=""
forge_forge_events_broker_published, forge_forge_events_broker_publish_duration the same histogram, filtered by messaging_system
forge_forge_events_nats_messages_published, forge_forge_events_nats_publish_duration the same histogram, filtered by messaging_system
forge_forge_events_nats_messages_received, forge_forge_events_nats_receive_duration messaging_process_duration_seconds
forge_forge_events_redis_messages_published, forge_forge_events_redis_publish_duration the same histogram, filtered by messaging_system
forge_forge_events_redis_messages_received messaging_process_duration_seconds

publish_failures, publish_broker_errors, the handler and receive error counters, and the subscription and connection series are still there.

Schema validation

The schema subpackage has a schema registry (schema.NewSchemaRegistry) with versioned event schemas and an EventSchemaValidator that checks event data against them. The bus doesn't call it for you, so run it in a handler middleware or before you publish. The features page covers it.

Documentation

Topic Where
Overview, what's registered and runtime notes Events extension docs
Bus, brokers, store, snapshots, handlers and middleware Features
YAML, Redis delivery modes, store ownership and unsubscribe retries Configuration
Function reference Functions and pkg.go.dev

Examples

Example What you'll learn
events-demo Starting the extension, subscribing, publishing five events and reading store and bus stats

It has its own go.mod. Run it with GOWORK=off go run . from its directory.

FAQ

How do I build an event-driven Go service with Forge?

Add events.NewExtension() to your app, subscribe named handlers from an after-start hook with events.GetEventBus, and publish core.NewEvent(...) from your handlers. The quick start is a complete program that needs nothing but Go.

Does Forge Events support event sourcing?

Yes. Every published event is saved to the event store first, and you can read an aggregate's history back with GetEventsByAggregate, take snapshots with CreateSnapshot, and query by type or time range. The built-in store is in memory, so for anything you need to keep, pass your own core.EventStore with WithEventStore.

Will a failed handler get the event again?

On the memory, NATS and Redis Pub/Sub brokers, no: the error is logged and the event is gone, so give the handler a RetryPolicy. On Redis Streams, yes. The message stays pending until the handler succeeds or runs out of attempts and moves to the dead-letter stream.

Can I use NATS JetStream or Kafka as the broker?

Not through this extension today. The built-in broker types are memory, nats (core NATS, not JetStream) and redis. For Kafka, use the Kafka extension directly, or implement core.MessageBroker and register it with bus.RegisterBroker while the bus is stopped.

Contributing

The extension is its own Go module inside the Forge repository, with brokers, stores, schema and core as subpackages. Test it from its directory:

cd extensions/events
GOWORK=off go build ./...
GOWORK=off go vet ./...
GOWORK=off go test -short -count=1 ./...

The Redis integration tests in brokers skip unless FORGE_EVENTS_REDIS_TEST_ADDR points at a Redis server. Run golangci-lint run ./... from the same directory before you open a pull request, and see AGENTS.md for the toolchain, lint rules and commit format.

License

Forge Events is part of Forge and is available under the Apache License 2.0.

Documentation

Index

Constants

View Source
const (
	// ServiceKey is the DI key for the event service.
	ServiceKey = "events"
	// EventBusKey is the DI key for the event bus.
	EventBusKey = "eventBus"
	// EventStoreKey is the DI key for the event store.
	EventStoreKey = "eventStore"
	// HandlerRegistryKey is the DI key for the handler registry.
	HandlerRegistryKey = "eventHandlerRegistry"
)

DI container keys for events extension services.

Variables

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

	// ErrSnapshotNotFound is returned when a snapshot is not found.
	ErrSnapshotNotFound = errors.New("snapshot not found")

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

	// ErrInvalidSnapshot is returned when a snapshot is invalid.
	ErrInvalidSnapshot = errors.New("invalid snapshot")

	// ErrBrokerNotFound is returned when a broker is not found.
	ErrBrokerNotFound = errors.New("broker not found")

	// ErrBrokerAlreadyRegistered is returned when a broker is already registered.
	ErrBrokerAlreadyRegistered = errors.New("broker already registered")

	// ErrHandlerNotFound is returned when a handler is not found.
	ErrHandlerNotFound = errors.New("handler not found")

	// ErrEventBusNotStarted is returned when the event bus is not started.
	ErrEventBusNotStarted = errors.New("event bus not started")

	// ErrEventStoreNotStarted is returned when the event store is not started.
	ErrEventStoreNotStarted = errors.New("event store not started")
)

Functions

func GetEventBus

func GetEventBus(c forge.Container) (core.EventBus, error)

GetEventBus retrieves the EventBus from the container. Returns error if not found or type assertion fails.

func GetEventStore

func GetEventStore(c forge.Container) (core.EventStore, error)

GetEventStore retrieves the EventStore from the container. Returns error if not found or type assertion fails.

func GetHandlerRegistry

func GetHandlerRegistry(c forge.Container) (*core.HandlerRegistry, error)

GetHandlerRegistry retrieves the HandlerRegistry from the container. Returns error if not found or type assertion fails.

func MustGetEventBus

func MustGetEventBus(c forge.Container) core.EventBus

MustGetEventBus retrieves the EventBus from the container. Panics if not found or type assertion fails.

func MustGetEventStore

func MustGetEventStore(c forge.Container) core.EventStore

MustGetEventStore retrieves the EventStore from the container. Panics if not found or type assertion fails.

func MustGetHandlerRegistry

func MustGetHandlerRegistry(c forge.Container) *core.HandlerRegistry

MustGetHandlerRegistry retrieves the HandlerRegistry from the container. Panics if not found or type assertion fails.

func NewEventBus

func NewEventBus(config EventBusOptions) (core.EventBus, error)

NewEventBus creates a new event bus.

func NewExtension

func NewExtension() forge.Extension

NewExtension creates a new events extension.

func NewExtensionWithConfig

func NewExtensionWithConfig(config Config, options ...EventServiceOption) forge.Extension

NewExtensionWithConfig creates a new events extension with custom config.

func WrapError

func WrapError(err error, message string) error

WrapError wraps an error with additional context.

Types

type BrokerConfig

type BrokerConfig struct {
	Name     string         `json:"name"     yaml:"name"`
	Type     string         `json:"type"     yaml:"type"` // memory, nats, redis
	Enabled  bool           `json:"enabled"  yaml:"enabled"`
	Priority int            `json:"priority" yaml:"priority"`
	Config   map[string]any `json:"config"   yaml:"config"`
}

BrokerConfig defines configuration for message brokers.

type BusConfig

type BusConfig struct {
	DefaultBroker string        `json:"default_broker" yaml:"default_broker"`
	MaxRetries    int           `json:"max_retries"    yaml:"max_retries"`
	RetryDelay    time.Duration `json:"retry_delay"    yaml:"retry_delay"`
	EnableMetrics bool          `json:"enable_metrics" yaml:"enable_metrics"`
	EnableTracing bool          `json:"enable_tracing" yaml:"enable_tracing"`
	// Deprecated: the bus has no worker pool; this field does nothing.
	BufferSize int `json:"buffer_size" yaml:"buffer_size"`
	// Deprecated: the bus has no worker pool; this field does nothing.
	WorkerCount       int           `json:"worker_count"       yaml:"worker_count"`
	ProcessingTimeout time.Duration `json:"processing_timeout" yaml:"processing_timeout"`
}

BusConfig defines configuration for the event bus.

type Config

type Config struct {
	// Event Bus configuration
	Bus BusConfig `json:"bus" yaml:"bus"`

	// Event Store configuration
	Store StoreConfig `json:"store" yaml:"store"`

	// Message Brokers
	Brokers []BrokerConfig `json:"brokers" yaml:"brokers"`

	// Metrics
	Metrics MetricsConfig `json:"metrics" yaml:"metrics"`
}

Config defines the configuration for the events extension.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns default configuration.

func (Config) Validate added in v1.12.5

func (c Config) Validate() error

Validate checks route configuration before the service opens dependencies.

type DomainEventHandler

type DomainEventHandler = core.DomainEventHandler

DomainEventHandler is a specialized handler for domain events.

func NewDomainEventHandler

func NewDomainEventHandler(name, aggregateType string, eventTypes []string, handler EventHandlerFunc) *DomainEventHandler

NewDomainEventHandler creates a new domain event handler.

type EventBusConfig

type EventBusConfig struct {
	DefaultBroker string        `json:"default_broker" yaml:"default_broker"`
	MaxRetries    int           `json:"max_retries"    yaml:"max_retries"`
	RetryDelay    time.Duration `json:"retry_delay"    yaml:"retry_delay"`
	EnableMetrics bool          `json:"enable_metrics" yaml:"enable_metrics"`
	EnableTracing bool          `json:"enable_tracing" yaml:"enable_tracing"`
	// Deprecated: the bus has no worker pool; this field does nothing.
	BufferSize int `json:"buffer_size" yaml:"buffer_size"`
	// Deprecated: the bus has no worker pool; this field does nothing.
	WorkerCount       int           `json:"worker_count"       yaml:"worker_count"`
	ProcessingTimeout time.Duration `json:"processing_timeout" yaml:"processing_timeout"`
}

EventBusConfig defines configuration for the event bus.

type EventBusImpl

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

EventBusImpl implements EventBus.

func (*EventBusImpl) Dependencies

func (eb *EventBusImpl) Dependencies() []string

Dependencies implements core.Service.

func (*EventBusImpl) GetBroker

func (eb *EventBusImpl) GetBroker(name string) (core.MessageBroker, error)

GetBroker implements EventBus.

func (*EventBusImpl) GetBrokers

func (eb *EventBusImpl) GetBrokers() map[string]core.MessageBroker

GetBrokers implements EventBus.

func (*EventBusImpl) GetStats

func (eb *EventBusImpl) GetStats() map[string]any

GetStats implements EventBus.

func (*EventBusImpl) HealthCheck

func (eb *EventBusImpl) HealthCheck(ctx context.Context) error

HealthCheck implements core.EventBus.

func (*EventBusImpl) Name

func (eb *EventBusImpl) Name() string

Name implements core.Service.

func (*EventBusImpl) Publish

func (eb *EventBusImpl) Publish(ctx context.Context, event *core.Event) error

Publish implements EventBus.

func (*EventBusImpl) PublishTo

func (eb *EventBusImpl) PublishTo(ctx context.Context, brokerName string, event *core.Event) error

PublishTo implements EventBus.

func (*EventBusImpl) RegisterBroker

func (eb *EventBusImpl) RegisterBroker(name string, broker core.MessageBroker) error

RegisterBroker implements EventBus.

func (*EventBusImpl) SetDefaultBroker

func (eb *EventBusImpl) SetDefaultBroker(name string) error

SetDefaultBroker implements EventBus.

func (*EventBusImpl) Start

func (eb *EventBusImpl) Start(ctx context.Context) error

Start connects brokers and restores subscriptions. This method is idempotent - calling it multiple times is safe.

func (*EventBusImpl) Stop

func (eb *EventBusImpl) Stop(ctx context.Context) error

Stop cancels admitted operations and waits for broker shutdown.

func (*EventBusImpl) Subscribe

func (eb *EventBusImpl) Subscribe(eventType string, handler core.EventHandler) error

Subscribe implements EventBus.

func (*EventBusImpl) UnregisterBroker

func (eb *EventBusImpl) UnregisterBroker(name string) error

UnregisterBroker implements EventBus.

func (*EventBusImpl) Unsubscribe

func (eb *EventBusImpl) Unsubscribe(eventType string, handlerName string) error

Unsubscribe implements EventBus.

type EventBusOptions

type EventBusOptions struct {
	Store           core.EventStore
	HandlerRegistry *core.HandlerRegistry
	Logger          forge.Logger
	Metrics         forge.Metrics
	Config          EventBusConfig
}

EventBusOptions defines configuration for EventBusImpl.

type EventCriteria

type EventCriteria = core.EventCriteria

EventCriteria defines criteria for querying events.

func NewEventCriteria

func NewEventCriteria() *EventCriteria

NewEventCriteria creates a new event criteria.

type EventHandler

type EventHandler = core.EventHandler

EventHandler defines the interface for handling events.

type EventHandlerFunc

type EventHandlerFunc = core.EventHandlerFunc

EventHandlerFunc is a function adapter for EventHandler.

type EventProjection

type EventProjection = core.EventProjection

EventProjection represents a projection of events.

type EventService

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

EventService provides event-driven architecture capabilities.

func GetEventService

func GetEventService(c forge.Container) (*EventService, error)

GetEventService retrieves the EventService from the container. Returns error if not found or type assertion fails.

func GetEventServiceFromApp

func GetEventServiceFromApp(app forge.App) (*EventService, error)

GetEventServiceFromApp retrieves the EventService from the app. Returns error if not found or type assertion fails.

func MustGetEventService

func MustGetEventService(c forge.Container) *EventService

MustGetEventService retrieves the EventService from the container. Panics if not found or type assertion fails.

func MustGetEventServiceFromApp

func MustGetEventServiceFromApp(app forge.App) *EventService

MustGetEventServiceFromApp retrieves the EventService from the app. Panics if not found or type assertion fails.

func NewEventService

func NewEventService(config Config, logger forge.Logger, metrics forge.Metrics, options ...EventServiceOption) *EventService

NewEventService creates a new event service.

func (*EventService) GetEventBus

func (es *EventService) GetEventBus() core.EventBus

GetEventBus returns the event bus.

func (*EventService) GetEventStore

func (es *EventService) GetEventStore() core.EventStore

GetEventStore returns the event store.

func (*EventService) GetHandlerRegistry

func (es *EventService) GetHandlerRegistry() *core.HandlerRegistry

GetHandlerRegistry returns the handler registry.

func (*EventService) HealthCheck

func (es *EventService) HealthCheck(ctx context.Context) error

HealthCheck checks the health of the event service.

func (*EventService) Start

func (es *EventService) Start(ctx context.Context) error

Start starts the event service. This method is idempotent - calling it multiple times is safe.

func (*EventService) Stop

func (es *EventService) Stop(ctx context.Context) error

Stop stops the event service.

type EventServiceOption added in v1.12.5

type EventServiceOption func(*EventService)

EventServiceOption configures event service dependencies.

func WithEventStore added in v1.12.5

func WithEventStore(store core.EventStore) EventServiceOption

WithEventStore borrows a store owned by the caller. The service checks its health on startup but never closes it, including on failed startup.

func WithEventStoreFactory added in v1.12.5

func WithEventStoreFactory(factory EventStoreFactory) EventServiceOption

WithEventStoreFactory constructs an owned store for each service run.

type EventStore

type EventStore = core.EventStore

EventStore defines the interface for persisting and retrieving events.

type EventStoreConfig

type EventStoreConfig = core.EventStoreConfig

EventStoreConfig defines configuration for event stores.

func DefaultEventStoreConfig

func DefaultEventStoreConfig() *EventStoreConfig

DefaultEventStoreConfig returns default configuration.

type EventStoreFactory added in v1.12.5

type EventStoreFactory func(context.Context, StoreConfig) (core.EventStore, error)

EventStoreFactory constructs a store owned by the event service. It is called again after a completed shutdown so each run receives a usable store.

type EventStoreMetrics

type EventStoreMetrics = core.EventStoreMetrics

EventStoreMetrics defines metrics for event stores.

type EventStoreMigration

type EventStoreMigration = core.EventStoreMigration

EventStoreMigration represents a migration for the event store.

type EventStoreMigrator

type EventStoreMigrator = core.EventStoreMigrator

EventStoreMigrator manages event store migrations.

type EventStoreStats

type EventStoreStats = core.EventStoreStats

EventStoreStats represents statistics for an event store.

type EventStoreTransaction

type EventStoreTransaction = core.EventStoreTransaction

EventStoreTransaction represents a transaction in the event store.

type EventStream

type EventStream = core.EventStream

EventStream represents a stream of events.

type EventStreamConfig

type EventStreamConfig = core.EventStreamConfig

EventStreamConfig defines configuration for event streams.

func DefaultEventStreamConfig

func DefaultEventStreamConfig() *EventStreamConfig

DefaultEventStreamConfig returns default stream configuration.

type EventStreamHandler

type EventStreamHandler = core.EventStreamHandler

EventStreamHandler handles events from an event stream.

type Extension

type Extension struct {
	*forge.BaseExtension
	// contains filtered or unexported fields
}

Extension implements forge.Extension for events. The extension is now a lightweight facade that loads config and registers services.

func (*Extension) Health

func (e *Extension) Health(ctx context.Context) error

Health checks the health of the extension.

func (*Extension) Register

func (e *Extension) Register(app forge.App) error

Register registers the extension with the app.

func (*Extension) Start

func (e *Extension) Start(ctx context.Context) error

Start resolves and starts the event service, then marks the extension as started.

func (*Extension) Stop

func (e *Extension) Stop(ctx context.Context) error

Stop stops the event service and marks the extension as stopped.

type HandlerMiddleware

type HandlerMiddleware = core.HandlerMiddleware

HandlerMiddleware defines middleware for event handlers.

func LoggingMiddleware

func LoggingMiddleware(l forge.Logger) HandlerMiddleware

LoggingMiddleware creates logging middleware for handlers.

func MetricsMiddleware

func MetricsMiddleware(metrics forge.Metrics) HandlerMiddleware

MetricsMiddleware creates metrics middleware for handlers.

func ValidationMiddleware

func ValidationMiddleware() HandlerMiddleware

ValidationMiddleware creates validation middleware for handlers.

type HandlerRegistry

type HandlerRegistry = core.HandlerRegistry

HandlerRegistry manages event handlers.

func NewHandlerRegistry

func NewHandlerRegistry(logger forge.Logger, metrics forge.Metrics) *HandlerRegistry

NewHandlerRegistry creates a new handler registry.

type MetricsConfig

type MetricsConfig struct {
	Enabled          bool          `json:"enabled"            yaml:"enabled"`
	PublishInterval  time.Duration `json:"publish_interval"   yaml:"publish_interval"`
	EnablePerType    bool          `json:"enable_per_type"    yaml:"enable_per_type"`
	EnablePerHandler bool          `json:"enable_per_handler" yaml:"enable_per_handler"`
}

MetricsConfig defines configuration for event metrics.

type ProjectionManager

type ProjectionManager = core.ProjectionManager

ProjectionManager manages event projections.

type ReflectionEventHandler

type ReflectionEventHandler = core.ReflectionEventHandler

ReflectionEventHandler uses reflection to call methods based on event type.

func NewReflectionEventHandler

func NewReflectionEventHandler(name string, target any) *ReflectionEventHandler

NewReflectionEventHandler creates a new reflection-based event handler.

type RetryPolicy

type RetryPolicy = core.RetryPolicy

RetryPolicy defines retry behavior for event handlers.

func NewRetryPolicy

func NewRetryPolicy(maxRetries int, initialDelay time.Duration) *RetryPolicy

NewRetryPolicy creates a new retry policy.

type Snapshot

type Snapshot = core.Snapshot

Snapshot represents a point-in-time snapshot of aggregate state.

func NewSnapshot

func NewSnapshot(aggregateID, snapshotType string, data any, version int) *Snapshot

NewSnapshot creates a new snapshot.

type StoreConfig

type StoreConfig struct {
	Type     string `json:"type"     yaml:"type"`     // memory, postgres, mongodb
	Database string `json:"database" yaml:"database"` // Database connection name (from database extension)
	Table    string `json:"table"    yaml:"table"`
}

StoreConfig defines configuration for the event store.

func (*StoreConfig) ToCoreStoreConfig

func (c *StoreConfig) ToCoreStoreConfig() *core.EventStoreConfig

ToCoreStoreConfig converts to core.EventStoreConfig.

type TransactionalEventStore

type TransactionalEventStore = core.TransactionalEventStore

TransactionalEventStore extends EventStore with transaction support.

type TypedEventHandler

type TypedEventHandler = core.TypedEventHandler

TypedEventHandler is a handler for specific event types.

func NewTypedEventHandler

func NewTypedEventHandler(name string, eventTypes []string, handler EventHandlerFunc) *TypedEventHandler

NewTypedEventHandler creates a new typed event handler.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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