Skip to content

catalog — Bookstore Platform v2 catalog service

The catalog service for the Bookstore Platform v2 reference application: a thin Go service that owns the books entity, fronts it with a JSON+REST API, persists it in Postgres, and exposes Prometheus metrics on /metrics.

Promoted from the Phase 16 stub to a real, tested, benchmarked service in May 2026 — the first of the Platform v2 services to graduate. See BENCHMARKS.md for the empirical numbers.

API

Method Path Description Body Returns
GET /healthz Liveness — process up and serving 200 {"status":"ok"}
GET /readyz Readiness — DB round-trip succeeds 200 {"status":"ok"} / 503
GET /metrics Prometheus scrape 200 text/plain
GET /books List books (paginated) ?limit=N&offset=M 200 {"books":[…],"limit":N,"offset":M}
GET /books/{id} Fetch one book 200 Book / 404
POST /books Create Book JSON 201 Book / 400
PUT /books/{id} Replace Book JSON (id from path) 200 Book / 404 / 400
DELETE /books/{id} Delete 204 / 404

Book shape:

{
  "id":     "string (required, free-form)",
  "title":  "string (required)",
  "author": "string (required)",
  "isbn":   "string (required, 10 or 13 digits — hyphens and spaces stripped server-side)"
}

Configuration

Env var Default Meaning
PORT 8080 HTTP listen port
DB_DSN (unset → in-memory repo) Postgres connection string. Format: postgres://user:pass@host:port/db?sslmode=disable&pool_max_conns=25. Empty → service starts with a 3-book in-memory seed (handy for local dev / smoke tests).

Run it

Locally, no DB needed

go run .
# service comes up on :8080 with three seeded books

Locally, against a real Postgres

docker run -d --name catalog-pg -p 5432:5432 \
  -e POSTGRES_USER=catalog -e POSTGRES_PASSWORD=catalog -e POSTGRES_DB=catalog \
  postgres:16-alpine

export DB_DSN="postgres://catalog:catalog@localhost:5432/catalog?sslmode=disable"
go run .

The service applies an idempotent CREATE TABLE IF NOT EXISTS books (…) on boot, so the first run on a fresh DB is enough — no separate migration step required. In production, the migration would live in a Kubernetes Job (see Part 06.04 of the guide for the pattern).

In Kubernetes

kubectl apply -f deployment.yaml -f service.yaml
# (Deployment expects an image at bookstore/catalog:dev — build with
#  `docker build -t bookstore/catalog:dev .`)

Develop

# unit tests (fast; no Docker)
go test -short ./...

# integration tests (spins up a real Postgres via testcontainers-go; needs Docker)
go test ./...

# coverage
go test -cover -coverprofile=cover.out ./...
go tool cover -html=cover.out

# benchmarks (see BENCHMARKS.md for the latest committed numbers)
go test -run='^$' -bench=. -benchmem ./...

# end-to-end load test (k6 required)
k6 run bench/load.js

Layout

catalog/
├── BENCHMARKS.md           — committed perf numbers + methodology
├── Dockerfile              — multi-stage, distroless final image, nonroot
├── README.md               — this file
├── bench/load.js           — k6 end-to-end load test
├── deployment.yaml         — Kubernetes Deployment manifest
├── service.yaml            — Kubernetes Service manifest
├── go.mod, go.sum          — dependency lock
├── main.go                 — entry point + wiring
├── handlers.go             — HTTP handlers + middleware
├── handlers_test.go        — handler tests (table-driven, in-memory repo)
├── model.go                — domain types (Book) + validation
├── model_test.go           — validation tests
├── repository.go           — Repository interface + PostgresRepo + MemRepo
├── repository_test.go      — integration tests (testcontainers-go + Postgres)
├── metrics.go              — Prometheus collectors + /metrics handler
└── bench_test.go           — Go in-process benchmarks

Observability

  • Logs — JSON-structured via log/slog to stdout. One line per request with method, path, status, duration_ms.
  • Metrics — Prometheus exposition on /metrics. Custom series:
  • catalog_http_requests_total{method,path,status} — counter, status normalised to class (2xx, 4xx, 5xx) to keep label cardinality bounded.
  • catalog_http_request_duration_seconds{method,path} — histogram, buckets tuned to 1ms–5s.
  • Plus the standard go_* and process_* collectors.
  • Traces — not yet emitted; OpenTelemetry instrumentation is the next planned change (see the project CHANGELOG.md).

Tests + coverage

Last full-suite run (go test -count=1 -cover ./... with Docker available for integration tests): 75.0% statement coverage across the package, all unit + integration tests passing. The remaining 25% is main.go's process wiring + the Postgres bootstrap path — both exercised in the manual Kubernetes deploy rather than unit-level.

Where this fits in the guide

  • The architecture (one bounded context per service, repo pattern, in-memory fake for tests) is established in ch.13.01 ("the Bookstore Platform shape").
  • The deployment manifest + Helm-chart wiring is teased in ch.13.05.
  • The Prometheus + slog instrumentation pattern is from Part 06 (production-readiness) and Part 09 (observability).
  • The benchmark methodology + thresholds is the worked example for ch.06.05 ("how to put numbers on your service").