Go Guide

The Go implementation uses generics (Go 1.21+) for type-safe handler registration and dispatch. Handlers are plain functions; no interface implementations are required for basic usage.

The Go module lives in a dedicated public repository so that go get works without authentication. Import path: github.com/chandru415/slck-mediator/go/mediator

Table of contents

  1. Go Guide
    1. Architecture
    2. Quick Start
      1. 1. Define messages
      2. 2. Define handlers
      3. 3. Wire the mediator
      4. 4. Add middleware
    3. Streaming
    4. Code Generation
    5. Full API Reference

Architecture

flowchart TD
    subgraph "Your Application"
        CMD["CreateOrderCommand"] --> M[Mediator]
        QRY["GetOrderQuery"]      --> M
        EVT["OrderCreatedEvent"]  --> M
    end
    subgraph "Framework"
        M --> PL["Middleware Pipeline"]
        PL --> H1["RequestHandler"]
        PL --> H2["StreamHandler"]
        PL --> H3["NotificationHandler"]
    end
    H1 --> DB[(Database)]
    H1 --> EB[[Event Bus]]
    style M fill:#00ADD8,color:#fff

Quick Start

go get github.com/chandru415/slck-mediator/go/mediator@latest

1. Define messages

Messages are plain Go structs. No embedding or interface satisfaction is required.

package orders

// Command (returns a single value)
type CreateOrderCommand struct {
    UserID string
    Amount float64
}

// Query
type GetOrderQuery struct {
    OrderID string
}
type GetOrderResult struct {
    OrderID string
    Status  string
}

// Notification (dispatched to N handlers)
type OrderCreatedEvent struct {
    OrderID string
}

2. Define handlers

Handler functions receive a *RequestContext as the first argument, providing access to the mediator, logger, metrics, and tracer.

import framework "github.com/chandru415/slck-mediator/go/mediator"

func HandleCreateOrder(ctx *framework.RequestContext, cmd CreateOrderCommand) (string, error) {
    orderID := "order-" + cmd.UserID
    ctx.Logger.Info("order.created", map[string]any{"order_id": orderID})
    return orderID, nil
}

func HandleGetOrder(ctx *framework.RequestContext, q GetOrderQuery) (GetOrderResult, error) {
    return GetOrderResult{OrderID: q.OrderID, Status: "confirmed"}, nil
}

3. Wire the mediator

package main

import (
    "context"
    "fmt"
    framework "github.com/chandru415/slck-mediator/go/mediator"
)

func main() {
    logger  := framework.ConsoleLogger{}
    metrics := framework.NewInMemoryMetrics()
    tracer  := framework.NewInMemoryTracer("my-service", logger, metrics)

    m := framework.NewMediator(framework.Config{
        Logger:  logger,
        Metrics: metrics,
        Tracer:  tracer,
    })

    framework.RegisterRequestHandler[CreateOrderCommand, string](m, HandleCreateOrder)
    framework.RegisterRequestHandler[GetOrderQuery, GetOrderResult](m, HandleGetOrder)

    orderID, err := framework.Send[string](m, context.Background(), CreateOrderCommand{
        UserID: "user-1",
        Amount: 99.99,
    })
    if err != nil {
        panic(err)
    }
    fmt.Println(orderID)
}

4. Add middleware

m.RegisterPipeline(framework.NewLoggingMiddleware(logger))
m.RegisterPipeline(framework.NewRetryMiddleware(3, 100))
m.RegisterPipeline(framework.NewTimeoutMiddleware(5_000))

Streaming

Stream handlers write values to a channel. The framework closes the channel when the handler function returns.

func HandlePriceTicker(
    ctx *framework.RequestContext,
    req PriceTickerQuery,
    out chan<- float64,
) error {
    for i := 0; i < 5; i++ {
        out <- rand.Float64() * 100
        time.Sleep(200 * time.Millisecond)
    }
    return nil
}

framework.RegisterStreamHandler[PriceTickerQuery, float64](m, HandlePriceTicker)

ch, err := framework.Stream[float64](m, context.Background(), PriceTickerQuery{Symbol: "AAPL"})
for price := range ch {
    fmt.Println(price)
}

Code Generation

Add a //go:generate directive to generate a direct-dispatch GeneratedMediator:

//go:generate go run path/to/generate-mediator.go
go generate ./...

The generated mediator uses type-switch statements, eliminating all map lookups and interface assertions at dispatch time.

Never edit the generated file manually. Re-run go generate whenever you add, rename, or remove a handler or message type.


Full API Reference

See the README for the complete API including outbox, inbox, saga, event bus, and all middleware options.