diff --git a/docs/content/docs/getting-started.md b/docs/content/docs/getting-started.md
index 2301ced65..337e20997 100644
--- a/docs/content/docs/getting-started.md
+++ b/docs/content/docs/getting-started.md
@@ -2,7 +2,7 @@
title = "Getting started"
description = "Watermill up and running"
weight = -9999
-draft = false
+draft = false
toc = true
bref = "Watermill up and running"
type = "docs"
@@ -10,27 +10,27 @@ type = "docs"
### What is Watermill?
-Watermill is a Golang library for working efficiently with message streams. It is intended for building event-driven
-applications. It can be used for event sourcing, RPC over messages, sagas, and whatever else comes to your mind.
-You can use conventional pub/sub implementations like Kafka or RabbitMQ, but also HTTP or MySQL binlog, if that fits your use case.
-
-It comes with a set of Pub/Sub implementations and can be easily extended by your own.
+Watermill is a Go library for working with message streams.
+You can use it to build event-driven systems with popular Pub/Sub implementations like Kafka or RabbitMQ, as well as HTTP or Postgres if that fits your use case.
+It comes with a set of Pub/Sub implementations and can be easily extended.
Watermill also ships with standard middlewares like instrumentation, poison queue, throttling, correlation,
and other tools used by every message-driven application.
### Why use Watermill?
-With more projects adopting the microservices pattern over recent years, we realized that synchronous communication
-is not always the right choice. Asynchronous methods started to grow as a new standard way to communicate.
+When using microservices, synchronous communication is not always the right choice.
+Asynchronous methods became a new standard way to communicate.
+
+While there are many tools and libraries for synchronous communication, like HTTP, correctly setting up
+a message-oriented project can be challenging. There are many different message queues and streaming systems,
+each with different features, client libraries, and APIs.
-But while there's a lot of existing tooling for synchronous integration patterns (e.g. HTTP), correctly setting up
-a message-oriented project can be a challenge. There's a lot of different message queues and streaming systems,
-each with different features and client library API.
+Watermill aims to be the standard messaging library for Go, hiding all that complexity behind an API that is easy to understand.
+It provides all you need to build an application based on events or other asynchronous patterns.
-Watermill aims to be the standard messaging library for Go, hiding all that complexity behind an API that is easy to
-understand. It provides all you might need for building an application based on events or other asynchronous patterns.
-After looking at the examples, you should be able to quickly integrate Watermill with your project.
+**Watermill is NOT a framework**.
+It's a lightweight library that's easy to plug in or remove from your project.
### Install
@@ -38,15 +38,27 @@ After looking at the examples, you should be able to quickly integrate Watermill
go get -u github.com/ThreeDotsLabs/watermill
```
-### One Minute Background
+### One-Minute Background
-The basic idea behind event-driven applications stays always the same: listen for incoming messages and react to them.
+The idea behind event-driven applications is always the same: listen to and react to incoming messages.
Watermill supports this behavior for multiple [publishers and subscribers]({{< ref "/pubsubs" >}}).
-The core part of Watermill is the [*Message*]({{< ref "/docs/message" >}}). It is as important as `http.Request`
-is for the `http` package. Most Watermill features use this struct in some way.
+The core part of Watermill is the [*Message*]({{< ref "/docs/message" >}}).
+It is what `http.Request` is for the `net/http` package.
+Most Watermill features work with this struct.
+
+Watermill provides a few APIs for working with messages.
+They build on top of each other, each step providing a higher-level API:
+
+* At the bottom, the `Publisher` and `Subscriber` interfaces. It's the "raw" way of working with messages. You get full control, but also need to handle everything yourself.
+* The `Router` is similar to HTTP routers you probably know. It introduces message handlers.
+* The `CQRS` component adds generic handlers without needing to marshal and unmarshal messages yourself.
+
+
-Even though PubSub libraries come with complex features, for Watermill it's enough to implement two interfaces to start
+## Publisher & Subscriber
+
+Most Pub/Sub libraries come with complex features. For Watermill, it's enough to implement two interfaces to start
working with them: the `Publisher` and `Subscriber`.
```go
@@ -63,8 +75,9 @@ type Subscriber interface {
### Subscribing for Messages
-Let's start with subscribing. `Subscribe` expects a topic name and returns a channel of incoming messages.
-What _topic_ exactly means depends on the PubSub implementation.
+`Subscribe` expects a topic name and returns a channel of incoming messages.
+What _topic_ exactly means depends on the Pub/Sub implementation.
+Usually, it needs to match the topic name used by the publisher.
```go
messages, err := subscriber.Subscribe(ctx, "example.topic")
@@ -95,15 +108,15 @@ See detailed examples below for supported PubSubs.
Running in Docker
{{% /collapse-toggle %}}
{{% collapse-box id="docker" %}}
-The easiest way to run Watermill locally with Kafka is using Docker.
+The easiest way to run Watermill locally with Kafka is by using Docker.
{{% load-snippet file="src-link/_examples/pubsubs/kafka/docker-compose.yml" type="yaml" %}}
The source should go to `main.go`.
-To run, execute `docker-compose up` command.
+To run, execute the `docker-compose up` command.
-A more detailed explanation of how it is working (and how to add live code reload) can be found in [*Go Docker dev environment* article](https://threedots.tech/post/go-docker-dev-environment-with-go-modules-and-live-code-reloading/).
+A more detailed explanation of how it works (and how to add live code reload) can be found in the [*Go Docker dev environment* article](https://threedots.tech/post/go-docker-dev-environment-with-go-modules-and-live-code-reloading/).
{{% /collapse-box %}}
{{< /collapse >}}
@@ -126,7 +139,7 @@ The easiest way to run Watermill locally with NATS is using Docker.
The source should go to `main.go`.
-To run execute `docker-compose up` command.
+To run, execute the `docker-compose up` command.
A more detailed explanation of how it is working (and how to add live code reload) can be found in [*Go Docker dev environment* article](https://threedots.tech/post/go-docker-dev-environment-with-go-modules-and-live-code-reloading/).
{{% /collapse-box %}}
@@ -145,7 +158,7 @@ A more detailed explanation of how it is working (and how to add live code reloa
Running in Docker
{{% /collapse-toggle %}}
{{% collapse-box id="gcloud-streaming-docker" %}}
-You can run Google Cloud Pub/Sub emulator locally for development.
+You can run the Google Cloud Pub/Sub emulator locally for development.
{{% load-snippet file="src-link/_examples/pubsubs/googlecloud/docker-compose.yml" type="yaml" %}}
@@ -209,10 +222,10 @@ A more detailed explanation of how it is working (and how to add live code reloa
### Creating Messages
-Watermill doesn't enforce any message format. `NewMessage` expects a slice of bytes as the payload. You can use
-strings, JSON, protobuf, Avro, gob, or anything else that serializes to `[]byte`.
+Watermill doesn't enforce any message format. `NewMessage` expects a slice of bytes as the payload.
+You can use strings, JSON, protobuf, Avro, gob, or anything else that serializes to `[]byte`.
-The message UUID is optional, but recommended, as it helps with debugging.
+The message UUID is optional but recommended for debugging.
```go
msg := message.NewMessage(watermill.NewUUID(), []byte("Hello, world!"))
@@ -232,121 +245,109 @@ if err != nil {
{{< tabs id="publishing" tabs="go-channel,kafka,nats-streaming,gcloud,amqp,sql" labels="Go Channel,Kafka,NATS Streaming,Google Cloud Pub/Sub,RabbitMQ (AMQP),SQL" >}}
{{% tabs-tab id="go-channel"%}}
-{{% load-snippet-partial file="src-link/_examples/pubsubs/go-channel/main.go" first_line_contains="go process(messages)" last_line_contains="publisher.Publish" padding_after="4" %}}
+{{% load-snippet-partial file="src-link/_examples/pubsubs/go-channel/main.go" first_line_contains="message.NewMessage" last_line_contains="publisher.Publish" padding_after="2" %}}
{{% /tabs-tab %}}
{{% tabs-tab id="kafka" %}}
-{{% load-snippet-partial file="src-link/_examples/pubsubs/kafka/main.go" first_line_contains="go process(messages)" last_line_contains="publisher.Publish" padding_after="4" %}}
+{{% load-snippet-partial file="src-link/_examples/pubsubs/kafka/main.go" first_line_contains="message.NewMessage" last_line_contains="publisher.Publish" padding_after="2" %}}
{{% /tabs-tab %}}
{{% tabs-tab id="nats-streaming" %}}
-{{% load-snippet-partial file="src-link/_examples/pubsubs/nats-streaming/main.go" first_line_contains="go process(messages)" last_line_contains="publisher.Publish" padding_after="4" %}}
+{{% load-snippet-partial file="src-link/_examples/pubsubs/nats-streaming/main.go" first_line_contains="message.NewMessage" last_line_contains="publisher.Publish" padding_after="2" %}}
{{% /tabs-tab %}}
{{% tabs-tab id="gcloud" %}}
-{{% load-snippet-partial file="src-link/_examples/pubsubs/googlecloud/main.go" first_line_contains="go process(messages)" last_line_contains="publisher.Publish" padding_after="4" %}}
+{{% load-snippet-partial file="src-link/_examples/pubsubs/googlecloud/main.go" first_line_contains="message.NewMessage" last_line_contains="publisher.Publish" padding_after="2" %}}
{{% /tabs-tab %}}
{{% tabs-tab id="amqp" %}}
-{{% load-snippet-partial file="src-link/_examples/pubsubs/amqp/main.go" first_line_contains="go process(messages)" last_line_contains="publisher.Publish" padding_after="4" %}}
+{{% load-snippet-partial file="src-link/_examples/pubsubs/amqp/main.go" first_line_contains="message.NewMessage" last_line_contains="publisher.Publish" padding_after="2" %}}
{{% /tabs-tab %}}
{{% tabs-tab id="sql" %}}
-{{% load-snippet-partial file="src-link/_examples/pubsubs/sql/main.go" first_line_contains="go process(messages)" last_line_contains="publisher.Publish" padding_after="4" %}}
+{{% load-snippet-partial file="src-link/_examples/pubsubs/sql/main.go" first_line_contains="message.NewMessage" last_line_contains="publisher.Publish" padding_after="2" %}}
{{% /tabs-tab %}}
{{< /tabs >}}
-### Using *Message Router*
+### Router
-[*Publishers and subscribers*]({{< ref "/docs/pub-sub" >}}) are rather low-level parts of Watermill.
-In most cases, you'd usually want to use a high-level interface and features like [correlation, metrics, poison queue, retrying, throttling, etc.]({{< ref "/docs/messages-router#middleware" >}}).
+[*Publishers and subscribers*]({{< ref "/docs/pub-sub" >}}) are the low-level parts of Watermill.
+For most cases, you want to use a high-level API: [*Router*]({{< ref "/docs/messages-router" >}}) component.
-You might want to send an Ack only if the message was processed successfully.
-In other cases, you'll Ack immediately and then worry about processing.
-Sometimes, you want to perform some action based on the incoming message, and publish another message in response.
+#### Router configuration
-To handle these requirements, there is a component named [*Router*]({{< ref "/docs/messages-router" >}}).
+Start with configuring the router and adding plugins and middlewares.
-### Example application of *Message Router*
-The flow of the example application looks like this:
+A middleware is a function executed for each incoming message.
+You can use one of the existing ones for things like [correlation, metrics, poison queue, retrying, throttling, etc.]({{< ref "/docs/messages-router#middleware" >}}).
+You can also create your own.
+
+{{% render-md %}}
+{{% load-snippet-partial file="src-link/_examples/basic/3-router/main.go" first_line_contains="message.NewRouter" last_line_contains="middleware.Recoverer," padding_after="1" %}}
+{{% /render-md %}}
-1. A message is produced on topic `incoming_messages_topic` every second.
-2. `struct_handler` handler listens on `incoming_messages_topic`. When a message is received, the UUID is printed and a new message is produced on `outgoing_messages_topic`.
-3. `print_incoming_messages` handler listens on `incoming_messages_topic` and prints the messages' UUID, payload and metadata.
-4. `print_outgoing_messages` handler listens on `outgoing_messages_topic` and prints the messages' UUID, payload and metadata. Correlation ID should be the same as in the message on `incoming_messages_topic`.
+#### Handlers
-#### Router configuration
+Set up handlers that the router uses.
+Each handler independently handles incoming messages.
-Start with configuring the router, adding plugins and middlewares.
-Then set up handlers that the router will use. Each handler will independently handle messages.
+A handler listens to messages from the given subscriber and topic.
+Any messages returned from the handler function will be published to the given publisher and topic.
{{% render-md %}}
-{{% load-snippet-partial file="src-link/_examples/basic/3-router/main.go" first_line_contains="package" last_line_contains="router.Run(ctx)" padding_after="4" %}}
+{{% load-snippet-partial file="src-link/_examples/basic/3-router/main.go" first_line_contains="AddHandler returns" last_line_contains=")" padding_after="0" %}}
{{% /render-md %}}
-#### Incoming messages
+*Note: the example above uses one `pubSub` argument for both the subscriber and publisher.
+It's because we use the `GoChannel` implementation, which is a simple in-memory Pub/Sub.*
-The `struct_handler` consumes messages from `incoming_messages_topic`, so we are simulating incoming traffic by calling `publishMessages()` in the background.
-Notice that we've added the `SetCorrelationID` middleware. A Correlation ID will be added to all messages produced by the router (it will be stored in metadata).
+Alternatively, if you don't plan to publish messages from within the handler, you can use the simpler `AddNoPublisherHandler` method.
{{% render-md %}}
-{{% load-snippet-partial file="src-link/_examples/basic/3-router/main.go" first_line_contains="func publishMessages" last_line_contains="time.Sleep(time.Second)" padding_after="2" %}}
+{{% load-snippet-partial file="src-link/_examples/basic/3-router/main.go" first_line_contains="AddNoPublisherHandler" last_line_contains=")" padding_after="0" %}}
{{% /render-md %}}
-#### Handlers
-
-You may have noticed that there are two types of *handler functions*:
+You can use two types of *handler functions*:
-1. function `func(msg *message.Message) ([]*message.Message, error)`
-2. method `func (c structHandler) Handler(msg *message.Message) ([]*message.Message, error)`
+1. a function `func(msg *message.Message) ([]*message.Message, error)`
+2. a struct method `func (c structHandler) Handler(msg *message.Message) ([]*message.Message, error)`
-If your handler is a function without any dependencies, it's fine to use the first one.
-The second option is useful when your handler requires some dependencies like database handle, a logger, etc.
+Use the first one if your handler is a function without any dependencies.
+The second option is useful when your handler requires dependencies such as a database handle or a logger.
{{% render-md %}}
{{% load-snippet-partial file="src-link/_examples/basic/3-router/main.go" first_line_contains="func printMessages" last_line_contains="return message.Messages{msg}, nil" padding_after="3" %}}
{{% /render-md %}}
-#### Done!
-
-You can run this example by `go run main.go`.
-
-You've just created your first application with Watermill. You can find the full source in [/_examples/basic/3-router/main.go](https://github.com/ThreeDotsLabs/watermill/blob/master/_examples/basic/3-router/main.go).
+The complete example's source can be found at [/_examples/basic/3-router/main.go](https://github.com/ThreeDotsLabs/watermill/blob/master/_examples/basic/3-router/main.go).
### Logging
-To see Watermill's logs, you have to pass any logger that implements the [LoggerAdapter](https://github.com/ThreeDotsLabs/watermill/blob/master/log.go).
+To see Watermill's logs, pass any logger that implements the [LoggerAdapter](https://github.com/ThreeDotsLabs/watermill/blob/master/log.go).
For experimental development, you can use `NewStdLogger`.
-### Testing
-
-Watermill provides [a set of test scenarios](https://github.com/ThreeDotsLabs/watermill/blob/master/pubsub/tests/test_pubsub.go)
-that any Pub/Sub implementation can use. Each test suite needs to declare what features it supports and how to construct a new Pub/Sub.
-These scenarios check both basic usage and more uncommon use cases. Stress tests are also included.
-
-### Deployment
-
-Watermill is not a framework. We don't enforce any type of deployment and it's totally up to you.
+## What's next?
-### What's next?
+For more details, see [documentation topics]({{< ref "/docs" >}}).
-For more detailed documentation check [documentation topics]({{< ref "/docs" >}}).
+See the [CQRS component](/docs/cqrs) for another high-level API.
-#### Examples
+## Examples
Check out the [examples](https://github.com/ThreeDotsLabs/watermill/tree/master/_examples) that will show you how to start using Watermill.
The recommended entry point is [Your first Watermill application](https://github.com/ThreeDotsLabs/watermill/tree/master/_examples/basic/1-your-first-app).
-It contains the entire environment in `docker-compose.yml`, including Golang and Kafka, which you can run with one command.
+It contains the entire environment in `docker-compose.yml`, including Go and Kafka, which you can run with one command.
After that, you can see the [Realtime feed](https://github.com/ThreeDotsLabs/watermill/tree/master/_examples/basic/2-realtime-feed) example.
-It uses more middlewares and contains two handlers. There is also a separate application for publishing messages.
+It uses more middlewares and contains two handlers.
-For a different subscriber implementation, namely **HTTP**, refer to the [receiving-webhooks](https://github.com/ThreeDotsLabs/watermill/tree/master/_examples/real-world-examples/receiving-webhooks) example. It is a very simple application that saves webhooks to Kafka.
+For a different subscriber implementation (**HTTP**), see the [receiving-webhooks](https://github.com/ThreeDotsLabs/watermill/tree/master/_examples/real-world-examples/receiving-webhooks) example.
+It is a straightforward application that saves webhooks to Kafka.
-Full list of examples can be found in the project's [README](https://github.com/ThreeDotsLabs/watermill#examples).
+You can find the complete list of examples in the [README](https://github.com/ThreeDotsLabs/watermill#examples).
-#### Support
+## Support
-If anything is not clear, feel free to use any of our [support channels]({{< ref "/support" >}}), we will be glad to help.
+If anything is not clear, feel free to use any of our [support channels]({{< ref "/support" >}}); we will be glad to help.
diff --git a/docs/content/docs/pub-sub-implementing.md b/docs/content/docs/pub-sub-implementing.md
index d0cfe03c6..6531014ae 100644
--- a/docs/content/docs/pub-sub-implementing.md
+++ b/docs/content/docs/pub-sub-implementing.md
@@ -16,6 +16,12 @@ To add support for a custom Pub/Sub, you have to implement both `message.Publish
{{% load-snippet-partial file="src-link/message/pubsub.go" first_line_contains="type Publisher interface" last_line_contains="type SubscribeInitializer" padding_after="0" %}}
{{% /render-md %}}
+### Testing
+
+Watermill provides [a set of test scenarios](https://github.com/ThreeDotsLabs/watermill/blob/master/pubsub/tests/test_pubsub.go)
+that any Pub/Sub implementation can use. Each test suite needs to declare what features it supports and how to construct a new Pub/Sub.
+These scenarios check both basic usage and more uncommon use cases. Stress tests are also included.
+
### TODO list
Here are a few things you shouldn't forget about:
diff --git a/docs/layouts/partials/footer.html b/docs/layouts/partials/footer.html
index a07b3a0d9..27a398d7b 100644
--- a/docs/layouts/partials/footer.html
+++ b/docs/layouts/partials/footer.html
@@ -20,7 +20,9 @@
©Three Dots Labs MIT License