typevent provides type-safe event messaging channels for Go. They can be used to implement Pub/Sub schemes without the need for type assertions, streamlining the application code that uses the event channels.
At the core it consists of a generic Channel interface with the following methods:
type Channel[E Event] interface {
// Emit emits an event of type E on the channel.
Emit(E) error
// Subscribe registers a handler for events of type E on the channel.
Subscribe(ctx context.Context, handler Handler[E]) (Subscription, error)
}The package currently provides two implementations of the interface:
redis— uses Redis Pub/Sub as the backing distribution system and routes events by name across all clients connected to the same server.memory— routes events in-process with no external dependencies. The channel value itself is the bus: emitter and subscriber must share the sameChannelinstance (typically via dependency injection). Useful for single-process applications, tests, and local development.
Usage can look as follows:
import (
"context"
"fmt"
redisclient "github.com/redis/go-redis/v9"
"github.com/sehrgutesoftware/typevent/redis"
)
func ExampleNewChannel() {
type event string
// Create a new channel using redis Pub/Sub as the underlying event bus.
client := redisclient.NewClient(&redisclient.Options{Addr: "localhost:6379"})
// conf holds the redis client used by the channel
conf := redis.NewConfig(client)
// This is where we create the channel that can be used to emit and subscribe to events
channel := redis.NewChannel[event](conf, "CHANNEL_NAME")
// Register a subscriber for the channel.
sub, _ := channel.Subscribe(context.Background(), func(ctx context.Context, ev event) error {
fmt.Printf("subscriber says: %s\n", ev)
return nil
})
defer sub.Close()
// Emit an event on the channel.
channel.Emit("Hello World!")
}For single-process applications, the memory driver delivers events in-process without any external broker. There is no name-based routing — the channel value itself is the bus:
import (
"context"
"fmt"
"github.com/sehrgutesoftware/typevent/memory"
)
func ExampleNewChannel() {
type event string
// Create a new in-process channel. Share this value between emitter and
// subscriber via dependency injection or a package-level variable.
channel := memory.NewChannel[event]()
// Register a subscriber for the channel.
sub, _ := channel.Subscribe(context.Background(), func(ctx context.Context, ev event) error {
fmt.Printf("subscriber says: %s\n", ev)
return nil
})
defer sub.Close()
// Emit an event on the channel.
channel.Emit("Hello World!")
}go test ./...