Introduction
Watermill is a Go library for working efficiently with message streams. It provides a unified publisher/subscriber interface across multiple backends including Kafka, AMQP, Google Pub/Sub, NATS, and an in-memory implementation for testing. The goal is to let developers write event-driven code once and swap brokers without changing application logic.
What Watermill Does
- Provides a consistent Pub/Sub API across Kafka, RabbitMQ, NATS, Google Pub/Sub, and more
- Includes middleware for retries, throttling, deduplication, correlation, and metrics
- Supports CQRS and event sourcing patterns with built-in router and handler abstractions
- Ships with an in-memory Pub/Sub for fast integration testing without external services
- Offers a message router that dispatches events to typed handler functions
Architecture Overview
Watermill centers on a Router that subscribes to topics, dispatches messages to handler functions, and publishes results to output topics. Each handler receives a strongly typed Message containing payload, metadata, and an Ack/Nack interface. Middleware wraps handlers for cross-cutting concerns. The Publisher and Subscriber interfaces are implemented per broker, keeping application code decoupled from transport.
Self-Hosting & Configuration
- Add as a Go module dependency with
go get - Configure broker-specific adapters via struct options (no YAML or config files)
- Use the in-memory adapter for local development and unit tests
- Plug in OpenTelemetry or Prometheus middleware for observability
- Deploy as a standard Go binary with no sidecar or agent required
Key Features
- Unified Pub/Sub interface that decouples business logic from the message broker
- Built-in middleware chain for retries, poison queues, deduplication, and tracing
- First-class CQRS support with command bus, event bus, and event handler patterns
- In-memory Pub/Sub enables fast, deterministic tests with no infrastructure overhead
- Extensible adapter system for adding custom message sources or sinks
Comparison with Similar Tools
- Sarama / confluent-kafka-go — Kafka-only clients, no multi-broker abstraction
- go-micro — Full microservice framework with Pub/Sub, heavier footprint
- NATS Go client — NATS-specific, no cross-broker portability
- Benthos (Redpanda Connect) — Declarative stream processor with YAML config, not a Go library
- Temporal — Durable workflow engine, different paradigm from raw event streaming
FAQ
Q: Which message brokers does Watermill support? A: Kafka, AMQP 0.9.1 (RabbitMQ), Google Pub/Sub, NATS, NATS JetStream, Redis Streams, SQL, Bolt, and an in-memory implementation.
Q: Can I use Watermill without a running broker? A: Yes. The GoChannel (in-memory) Pub/Sub works without any external dependency and is suitable for testing and prototyping.
Q: How does Watermill handle message failures? A: Messages are Nacked and retried according to middleware configuration. A poison-queue middleware moves repeatedly failing messages to a dead-letter topic.
Q: Is Watermill production-ready? A: Yes. It is used in production at multiple companies and has been stable since v1.0 with a clear semantic versioning policy.