An open API service indexing awesome lists of open source software.

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.

Awesome Lists containing this project

README

          

# Dispatcher

A high-performance, type-safe dispatcher for Go.

[![Go Reference](https://pkg.go.dev/badge/github.com/struct0x/dispatcher.svg)](https://pkg.go.dev/github.com/struct0x/dispatcher)
[![Go Report Card](https://goreportcard.com/badge/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.