LocalBus
The LocalBus is an in-process event dispatcher for domain events and internal decoupling.
Creating a LocalBus
bus := event.NewLocalBus(event.LocalBusOptions{
Invoker: chain,
MaxConcurrent: 16,
OnErr: func(ctx context.Context, evt event.Event, err error, handler string) {
slog.Error("handler failed", "event", evt.Name(), "handler", handler, "error", err)
},
})
Options
| Field | Type | Default | Description |
|---|---|---|---|
Invoker |
event.Invoker |
required | The invoker chain for handler execution |
MaxConcurrent |
int |
16 | Maximum concurrent async handlers |
OnErr |
func(...) |
nil | Global error callback |
Dispatch Modes
Asynchronous (Emit)
bus.Emit(ctx, UserCreated{UserID: "123"})
Fire-and-forget. Handlers run in goroutines, limited by MaxConcurrent. The semaphore prevents unbounded goroutine growth.
Synchronous (EmitSync)
bus.EmitSync(ctx, UserCreated{UserID: "123"})
Waits for all handlers to complete before returning. Handlers run sequentially in the calling goroutine.
Concurrency Model
The MaxConcurrent setting controls a semaphore channel. When Emit is called:
- For each handler, attempt to acquire a semaphore slot
- If a slot is available, run the handler in a goroutine
- If no slot is available, block until one frees up (or context is cancelled)
- Release the slot when the handler completes
Error Handling
Errors from handlers are reported via:
- The
OnErrcallback (if set) - The event’s
OnErrmethod (if the event implementsOnErr)
Errors do not propagate to the caller of Emit or EmitSync.
RegisterSubscribers
Bulk registration for organized handler grouping:
bus.RegisterSubscribers(ctx,
NewEmailSubscriber(),
NewBillingSubscriber(),
)
Each subscriber’s Name() is used as the handler name. Subscribers implementing SubscribeOptionsProvider will have their options merged automatically.
Close
Close() is a no-op for LocalBus since there are no external connections.