Watermill, the NATS client and kafka-go compared for event-driven Go services, plus how Encore declares topics and subscriptions in Go and provisions the broker in your own AWS or GCP account.

Event-driven design lets services react to what happened without calling each other directly, and Martin Fowler's What do you mean by "Event-Driven"? separates event notification, event-carried state transfer and event sourcing, which tend to get mixed together under one name.
Go has strong broker clients for this, and most of the choice is between a library that abstracts the broker and a client for one specific broker. Where the broker runs is a separate question, and it decides what delivery semantics you get: Redis Pub/Sub, for example, documents at-most-once delivery, while Kafka keeps events on disk for replay.
What to weigh when choosing:
Watermill provides Publisher and Subscriber interfaces with implementations for Kafka, NATS, RabbitMQ, SQL databases and cloud brokers, plus a router with middleware.
Messages are bytes with marshalers on top, and the broker itself is still yours to run.
The official NATS Go client supports core publish and subscribe, request-reply and JetStream streams and consumers.
Core NATS is fire-and-forget, so delivery guarantees require JetStream configuration.
kafka-gokafka-go from Segment offers readers and writers that follow Go's io conventions, with consumer groups.
Reader and Writer types with consumer groups.Retries and dead-letter handling are yours to implement, and Kafka is a heavy service to operate yourself.
Encore combines event handling in Go with the broker behind it through its Go backend framework with built-in infrastructure, where a topic is a typed package-level variable and a subscription is a declaration with a handler. encore run starts a local Pub/Sub broker and Postgres, and a deploy provisions SNS and SQS on AWS or Pub/Sub on Google Cloud in your own account. Separately from the client library, Encore can declare topics in Go and provision SNS/SQS or Pub/Sub in your account, whichever option above you pick.
package billing
import (
"context"
"time"
"encore.dev/pubsub"
"encore.dev/rlog"
)
type InvoicePaid struct {
InvoiceID string
Amount int64
}
var InvoicePaidTopic = pubsub.NewTopic[*InvoicePaid]("invoice-paid", pubsub.TopicConfig{
DeliveryGuarantee: pubsub.AtLeastOnce,
})
var _ = pubsub.NewSubscription(InvoicePaidTopic, "update-ledger",
pubsub.SubscriptionConfig[*InvoicePaid]{
Handler: UpdateLedger,
RetryPolicy: &pubsub.RetryPolicy{
MinBackoff: time.Second,
MaxRetries: 5,
},
})
func UpdateLedger(ctx context.Context, e *InvoicePaid) error {
rlog.Info("recording payment", "invoice", e.InvoiceID, "amount", e.Amount)
return nil
}
func MarkPaid(ctx context.Context, id string, amount int64) error {
_, err := InvoicePaidTopic.Publish(ctx, &InvoicePaid{InvoiceID: id, Amount: amount})
return err
}
NewSubscription, and tests in encore test run it against local infrastructure.Encore's Pub/Sub API covers topics and subscriptions with at-least-once delivery, or exactly-once within the provider's throughput limits, and it has no offset or seek API, so Kafka-style replay of past events is not part of it. Kafka's own replay is bounded by topic retention, seven days by default according to the log.retention.hours broker setting, and compacted topics keep only the latest value per key. Cloud resources are created at deploy time, and the cloud access sits with Encore's IAM role, not the agent. Each subscription is also listed in the service catalog.
Watermill is the most complete Go library for event-driven code, with one Publisher and Subscriber interface over Kafka, NATS, RabbitMQ, SQL and cloud brokers. The NATS client and kafka-go are direct clients for one broker each. Encore is not a competing framework but a backend framework with built-in infrastructure where topics and subscriptions are declared in Go and the broker is provisioned for you, using SNS and SQS on AWS or Pub/Sub on Google Cloud.
Kafka suits high-volume event streams that consumers replay from a log. NATS suits low-latency messaging and request-reply, with JetStream adding persistence. Both have mature Go clients.
Declare the subscription at package level with pubsub.NewSubscription, passing the topic, a unique subscription name and a SubscriptionConfig whose Handler is a function taking a context and the event. Encore starts the subscriber with the service, retries failed messages, and provisions the subscription with the topic.
Deploy to your cloud on AWS and Google Cloud
without managing infrastructure
Encore lets developers and agents define application infrastructure directly in code, then automatically provisions it from local development to production in your AWS or GCP account.