This project is a minimal Go implementation of the outbox pattern for an e‑commerce style engine.
It has two small Go services:
orderswrites orders and their corresponding outbox records in a single Postgres transaction.dispatchercontinuously polls the outbox table and publishes events to Redis pub/sub.
Both services are built as plain Go binaries in this module.
| Dispatcher worker logs | Redis client subscriber |
|---|---|
- Postgres – primary data store for orders and outbox messages.
- Redis – pub/sub transport; dispatcher publishes
orders.createdevents. - Orders service (
./orders)- Inserts into
orderstable. - Inserts a serialized
OrderEventintooutboxtable withstate = 'pending'.
- Inserts into
- Dispatcher service (
./dispatcher)- Periodically (every second) selects one
pendingoutbox row withFOR UPDATE SKIP LOCKED. - Publishes the message to Redis channel =
topic. - Marks the outbox row as
processed.
- Periodically (every second) selects one
Example Postgres schema that matches the code:
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
CREATE TABLE orders (
id UUID PRIMARY KEY,
product TEXT NOT NULL,
quantity INT NOT NULL
);
CREATE TABLE outbox (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
topic TEXT NOT NULL,
message BYTEA NOT NULL,
state TEXT NOT NULL DEFAULT 'pending',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
processed_at TIMESTAMPTZ
);
orders/main.gowrites to bothordersandoutboxinside a single transaction.
-
DATABASE_URL(required for both services)
Example:export DATABASE_URL="postgres://user:password@localhost:5432/outboxy?sslmode=disable"
-
REDIS_URL(optional fordispatcher)
If empty, the dispatcher useslocalhost:6379:export REDIS_URL="redis://localhost:6379"
From the project root (/home/atharvamhaske/Projects/outboxy):
-
Build both binaries
make build # or individually make build-orders make build-dispatcher -
Run tests (Ginkgo/Gomega based)
make test -
Run services (with env vars set)
# Orders service – creates an order and an outbox row make run-orders # Dispatcher – polls outbox and publishes to Redis make run-dispatcher
-
Go module tidy
make tidy
-
Start Postgres and Redis
- Postgres database
outboxywith the schema above. - Redis server (default:
localhost:6379).
- Postgres database
-
Set environment variables
export DATABASE_URL="postgres://user:password@localhost:5432/outboxy?sslmode=disable" export REDIS_URL="redis://localhost:6379"
-
Run dispatcher
make run-dispatcher
-
Subscribe to Redis channel to see events
redis-cli SUBSCRIBE orders.created
-
Trigger an order + outbox entry
make run-orders
-
Observe
- Dispatcher logs: publishing + marking outbox rows as processed.
redis-clisubscriber: receivesorders.createdJSON payloads.- DB:
orderscontains the new order.outboxrow moves frompendingtoprocessed.
You can also run the whole example using Docker.
- Docker
- Docker Compose (or
docker composewith recent Docker)
From the project root:
docker compose up --buildThis starts:
postgreswith databaseoutboxyredisdispatcherservice polling the outbox table and publishing to Redisorders-oncewhich runs once to create a sample order and outbox record
-
View logs:
docker compose logs -f dispatcher docker compose logs -f orders-once
-
Subscribe to the Redis channel from your host:
redis-cli -h localhost -p 6379 SUBSCRIBE orders.created
You should see an orders.created event published when orders-once runs, while the dispatcher marks the outbox record as processed.