zoobzio January 2, 2026 Edit this page

Quickstart

Requirements

Go 1.24 or later.

Installation

go get github.com/zoobz-io/herald

Basic Usage

package main

import (
    "context"
    "fmt"

    kafkago "github.com/segmentio/kafka-go"
    "github.com/zoobz-io/capitan"
    "github.com/zoobz-io/herald"
    "github.com/zoobz-io/herald/kafka"
)

type Order struct {
    ID    string  `json:"id"`
    Total float64 `json:"total"`
}

var (
    orderCreated = capitan.NewSignal("order.created", "New order placed")
    orderKey     = capitan.NewKey[Order]("order", "app.Order")
)

func main() {
    ctx := context.Background()

    // Create provider
    writer := &kafkago.Writer{
        Addr:  kafkago.TCP("localhost:9092"),
        Topic: "orders",
    }
    provider := kafka.New("orders", kafka.WithWriter(writer))
    defer provider.Close()

    // Publish capitan events to Kafka
    pub := herald.NewPublisher(provider, orderCreated, orderKey, nil)
    pub.Start()
    defer pub.Close()

    // Emit an event - automatically published
    capitan.Emit(ctx, orderCreated, orderKey.Field(Order{
        ID:    "ORD-123",
        Total: 99.99,
    }))

    capitan.Shutdown()
}

What's Happening

  1. NewSignal and NewKey define the event contract (from capitan)
  2. kafka.New creates a provider for Kafka communication
  3. NewPublisher bridges capitan events to the broker
  4. pub.Start() registers a capitan listener that forwards events
  5. capitan.Emit queues an event — herald publishes it to Kafka
  6. capitan.Shutdown drains pending events before exit

Subscribing

The reverse direction — broker to capitan:

// Create provider with reader
reader := kafkago.NewReader(kafkago.ReaderConfig{
    Brokers: []string{"localhost:9092"},
    Topic:   "orders",
    GroupID: "order-processor",
})
provider := kafka.New("orders", kafka.WithReader(reader))
defer provider.Close()

// Hook listener first
capitan.Hook(orderCreated, func(ctx context.Context, e *capitan.Event) {
    order, _ := orderKey.From(e)
    fmt.Printf("Received: %s\n", order.ID)
})

// Subscribe: broker messages become capitan events
sub := herald.NewSubscriber(provider, orderCreated, orderKey, nil)
sub.Start(ctx)
defer sub.Close()

Messages are deserialized, emitted as capitan events, and acknowledged on success.

Next Steps

  • Concepts — Publishers, subscribers, providers, and pipelines
  • Architecture — Internal design and data flow