A high-performance, type-safe dispatcher for Go.
Dispatcher is a Go library that provides a fast and efficient way to route values to their appropriate handlers based on type. It's designed with performance in mind, offering both thread-safe and immutable variants with different performance characteristics.
- Type Safety: Compile-time type safety with generic handlers
- Zero Allocations: Dispatch operations produce zero allocations in steady state
- High Performance: Optimized for concurrent access patterns
- Middleware Support: Chainable middleware for cross-cutting concerns
- Two Registry Types:
Registry
: Thread-safe, dynamic registrationSealedRegistry
: Immutable, zero mutex overhead
go get github.com/struct0x/dispatcher
package main
import (
"context"
"fmt"
"log"
"github.com/struct0x/dispatcher"
)
type UserCreated struct {
ID int
Name string
}
type OrderPlaced struct {
OrderID string
Amount float64
}
func main() {
// Create a new registry
reg := dispatcher.NewRegistry()
// Register handlers for different types
dispatcher.Register[UserCreated](reg, func(ctx context.Context, event UserCreated) error {
fmt.Printf("User created: %s (ID: %d)\n", event.Name, event.ID)
return nil
})
dispatcher.Register[OrderPlaced](reg, func(ctx context.Context, event OrderPlaced) error {
fmt.Printf("Order placed: %s for $%.2f\n", event.OrderID, event.Amount)
return nil
})
// Dispatch events
ctx := context.Background()
if err := dispatcher.Dispatch(reg, ctx, UserCreated{ID: 1, Name: "Alice"}); err != nil {
log.Fatal(err)
}
if err := dispatcher.Dispatch(reg, ctx, OrderPlaced{OrderID: "ORD-001", Amount: 99.99}); err != nil {
log.Fatal(err)
}
}
// Define middleware
loggingMiddleware := func(next dispatcher.HandlerFunc[UserCreated]) dispatcher.HandlerFunc[UserCreated] {
return func(ctx context.Context, event UserCreated) error {
fmt.Printf("Processing user: %s\n", event.Name)
err := next(ctx, event)
fmt.Printf("Finished processing user: %s\n", event.Name)
return err
}
}
// Register with middleware
dispatcher.Register[UserCreated](reg, handler, loggingMiddleware)
// After registering all handlers, seal the registry for better performance
sealedReg := reg.Seal()
// SealedRegistry has zero mutex overhead
if err := dispatcher.Dispatch(sealedReg, ctx, UserCreated{ID: 2, Name: "Bob"}); err != nil {
log.Fatal(err)
}
Dispatcher is optimized for high-performance scenarios:
- Zero Allocations: Dispatch operations after initialization produce zero allocations
- Concurrent Safe:
Registry
can be used safely across goroutines - Sealed Optimization:
SealedRegistry
eliminates all mutex overhead - Benchmark Results (Apple M2 Max):
Registry
:- 1 CPU: 21.46 ns/op
- 4 CPU: 64.80 ns/op
- 8 CPU: 122.0 ns/op
SealedRegistry
:- 1 CPU: 14.59 ns/op
- 4 CPU: 28.95 ns/op
- 8 CPU: 45.53 ns/op
Note: Performance may vary based on your system and workload. Run benchmarks on your target system for accurate measurements.
Run benchmarks with:
go test -bench=. -benchmem
- Registration: Handlers are registered for specific types using Go generics
- Type Mapping: Types are mapped to handlers using
reflecgt.Type
as keys - Dispatch: Values are routed to appropriate handlers based on their runtime type
- Middleware Chain: Middleware is applied in the order provided during registration
dispatcher.Registry
: Thread-safe registry for dynamic handler registrationdispatcher.SealedRegistry
: Immutable registry with zero mutex overheaddispatcher.HandlerFunc[T]
: Type-safe handler functiondispatcher.Middleware[T]
: Type-safe middleware function
dispatcher.NewRegistry()
: Creates a new thread-safe registrydispatcher.Register[T](reg, handler, middleware...)
: Registers a handler for type T⚠️ If called multiple times for the same typeT
, later registrations overwrite earlier ones.
dispatcher.Dispatch(reg, ctx, value)
: Dispatches a value to its registered handlerregistry.Seal()
: Creates a sealed immutable copy of a registry
- Event-driven architectures
- Message routing systems
- Command handlers in CQRS
- Plugin systems
- Any scenario requiring type-based dispatch
This project is licensed under the MIT License - see the LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request.