Skip to content

struct0x/dispatcher

Repository files navigation

Dispatcher

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

Go Reference Go Report Card

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

go get github.com/struct0x/dispatcher

Usage

Basic Example

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

// 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

// 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:

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 file for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

About

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

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages