https://github.com/struct0x/dispatcher
A high-performance, type-safe dispatcher for Go.
https://github.com/struct0x/dispatcher
dispatchers go golang golang-library
Last synced: 5 months ago
JSON representation
A high-performance, type-safe dispatcher for Go.
- Host: GitHub
- URL: https://github.com/struct0x/dispatcher
- Owner: struct0x
- License: mit
- Created: 2025-09-11T08:22:19.000Z (9 months ago)
- Default Branch: main
- Last Pushed: 2025-09-11T15:43:40.000Z (9 months ago)
- Last Synced: 2025-09-13T15:01:44.421Z (9 months ago)
- Topics: dispatchers, go, golang, golang-library
- Language: Go
- Homepage:
- Size: 7.81 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Dispatcher
A high-performance, type-safe dispatcher for Go.
[](https://pkg.go.dev/github.com/struct0x/dispatcher)
[](https://goreportcard.com/report/github.com/struct0x/dispatcher)
## Overview
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.
## Features
- **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 registration
- `SealedRegistry`: Immutable, zero mutex overhead
## Installation
```bash
go get github.com/struct0x/dispatcher
```
## Usage
### Basic Example
```go
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)
}
}
```
### Using Middleware
```go
// 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)
```
### Sealed Registry for Maximum Performance
```go
// 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)
}
```
## Performance
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:
```bash
go test -bench=. -benchmem
```
## How It Works
1. **Registration**: Handlers are registered for specific types using Go generics
2. **Type Mapping**: Types are mapped to handlers using `reflecgt.Type` as keys
3. **Dispatch**: Values are routed to appropriate handlers based on their runtime type
4. **Middleware Chain**: Middleware is applied in the order provided during registration
## API Reference
### Core Types
- `dispatcher.Registry`: Thread-safe registry for dynamic handler registration
- `dispatcher.SealedRegistry`: Immutable registry with zero mutex overhead
- `dispatcher.HandlerFunc[T]`: Type-safe handler function
- `dispatcher.Middleware[T]`: Type-safe middleware function
### Core Functions
- `dispatcher.NewRegistry()`: Creates a new thread-safe registry
- `dispatcher.Register[T](reg, handler, middleware...)`: Registers a handler for type T
- ⚠️ If called multiple times for the same type `T`, later registrations overwrite earlier ones.
- `dispatcher.Dispatch(reg, ctx, value)`: Dispatches a value to its registered handler
- `registry.Seal()`: Creates a sealed immutable copy of a registry
## Use Cases
- **Event-driven architectures**
- **Message routing systems**
- **Command handlers in CQRS**
- **Plugin systems**
- **Any scenario requiring type-based dispatch**
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.