Guides

Choosing a Transport

How to choose the right event transport — Memory, NATS, Redis Streams, or Kafka.

Choosing a Transport

Redelay supports four event transport backends. Module code never changes between them — only the bootstrap wiring differs. This guide helps you pick the right one.

Decision guide

Rendering diagram...

Transport comparison

MemoryNATS coreNATS JetStreamRedis StreamsKafka
Persistence✗✗✓✓✓
Consumer groups✓✗✓✓✓
At-least-once✓*✗✓✓✓
Replay from offset✗✗✓✓✓
Cluster support✗✓✓✓✓
Infra requiredNoneNATSNATSRedisKafka
Best forTestsLow-latency notificationsDurable; no KafkaLightweight teamsHigh-throughput production

*Memory at-least-once is within a single process only.

Memory

Use for all unit and integration tests. No infrastructure required.

go
import (
    "github.com/redelay/go-events/eventbus"
    "github.com/redelay/go-events/transport/memory"
)

tr := memory.New()
bus := eventbus.New(tr)

Supports full consumer-group semantics: multiple groups each receive every message; within a group, messages are dispatched round-robin across subscribers.

NATS core

Use when you need low-latency, fire-and-forget delivery and can accept that messages published when no subscriber is active are dropped.

go
import "github.com/redelay/go-events/transport/nats"

// From env (recommended):
t, err := nats.NewFromEnv()

// Explicit:
t, err := nats.New(nats.Config{
    URL:          "nats://localhost:4222",
    UseJetStream: false,
})
shell
# Environment
NATS_URL=nats://localhost:4222
NATS_JETSTREAM=false

NATS JetStream

Use when you want durable delivery, consumer groups, and replay without running Kafka. JetStream is built into the same NATS binary — enable it with one flag.

go
t, err := nats.New(nats.Config{
    URL:          "nats://nats:4222",
    UseJetStream: true,
    AckWait:      30 * time.Second,
    MaxDeliver:   3,
})
shell
# Environment
NATS_URL=nats://nats:4222
NATS_JETSTREAM=true
NATS_ACK_WAIT_SECONDS=30
NATS_MAX_DELIVER=3
# Cluster:
NATS_URL=nats://nats1:4222,nats://nats2:4222,nats://nats3:4222

JetStream streams are auto-created by the transport on first subscribe — no manual NATS admin required.

Redis Streams

Use when Redis is already part of your stack (caching, rate limiting, sessions) and throughput is moderate. Avoids running a separate message broker.

go
t, err := redis.NewFromEnv()

// Explicit:
t, err := redis.New(redis.Config{
    Addr:          "redis:6379",
    BlockDuration: 200 * time.Millisecond,
    BatchSize:     10,
    MaxLen:        10_000,
})
shell
# Environment — falls back to REDIS_URL if REDIS_STREAMS_URL is unset
REDIS_STREAMS_URL=redis://redis:6379
REDIS_STREAMS_BLOCK_MS=200
REDIS_STREAMS_BATCH_SIZE=10
REDIS_STREAMS_MAX_LEN=10000

Kafka

Use for high-throughput production workloads, long-term retention, or when integrating with an existing Kafka ecosystem (Kafka Connect, ksqlDB, Schema Registry, etc.).

go
t, err := kafka.NewFromEnv()

// Explicit:
t, err := kafka.New(kafka.Config{
    Brokers: []string{"kafka:9092"},
    GroupID: "my-service",
})
shell
# Environment — single broker
KAFKA_BOOTSTRAP_SERVERS=kafka:9092

# Cluster with SASL/TLS (Confluent Cloud, MSK, Aiven):
KAFKA_BOOTSTRAP_SERVERS=broker1:9092,broker2:9092,broker3:9092
KAFKA_SECURITY_PROTOCOL=sasl_ssl
KAFKA_SASL_MECHANISM=scram-sha-256
KAFKA_SASL_USERNAME=myuser
KAFKA_SASL_PASSWORD=mysecret

Local development (docker-compose)

The infra/docker-compose.yml starts all four backends. The API service is pre-configured with env vars for each:

yaml
environment:
  KAFKA_BOOTSTRAP_SERVERS: kafka:29092
  NATS_URL: nats://nats:4222
  REDIS_STREAMS_URL: redis://redis:6379

Switch transports by changing the bootstrap wiring in cmd/api/main.go — no module code changes required.

Integration tests

Running make test-integration in go-events/ starts each backend in a Docker container via testcontainers-go and runs the same 5 scenarios against every transport to ensure behavioral consistency.

shell
cd go-events && make test-integration

See go-events reference for the full list of test scenarios.

Transport vs coordination — different jobs

Transports deliver business events (user.created, order.paid). They're durable, ordered, and consumer-group aware. Picking Kafka vs NATS vs Redis here is about delivery semantics.

Coordination is a separate surface for cross-container state sync — invalidating caches, fanning out live metrics, watching the currently-published flow route. It lives in go-framework/runtime/coordination with its own COORD env switch (noop / nats / redis) and is independent of the TRANSPORT choice. A common deployment uses Kafka for events and NATS for coordination.

See Coordination reference for when and how to use it.