Flow
ServicesTemplate Service

Architecture

Binaries, directory layout, and platform wiring in the template service.

Binaries

BinaryPathRole
APIcmd/apiHTTP server + outbox publisher; flags -migrate, -openapi
Flowstatecmd/flowstateBackground worker (sample loop; health only)
domain1Listenercmd/domain1ListenerConsumes template-domain1-queue-listener and logs each message
domain2Listenercmd/domain2ListenerConsumes template-domain2-queue-listener and logs each message

Layout

services/template/
  domains/                    ← public per-domain contracts
    domain1/events/
    domain2/events/
  internal/
    domains/                  ← business logic (insert row + outbox event)
      domain1/
      domain2/
    handlers/                 ← per-operation types and bodies (auth not wired yet)
    command/                  ← idempotency + transaction
    publisher/                ← drain domain outboxes to the broker
    listener/                 ← domain1Listener / domain2Listener consume + log
    testdb/                   ← this service's migrations, over common/testdb
  messageBus/                 ← generated fanout topology
  migrations/
  db/                         ← sqlc generated queries

common/                       ← shared with every service, see ADR 0018
  logging/                    ← process logger: JSON deployed, console locally
  httpapi/                    ← middleware, error envelope, command and pagination
                                 contracts, API and spec construction, root + docs
  response/                   ← the HTTP {header, payload} envelope
  envelope/                   ← event wire format on the bus
  swaggerui/                  ← vendored Swagger UI assets
  reqmeta/  health/  testdb/

What is left in services/template/ is what only this service can answer: its domains, its schema, its outbox, its topology. The platform mechanics are in common/, so template and accounting cannot drift apart on them — see ADR 0018, and ADR 0019 for the part of handlers/ that moved with them.

Request path

Diagram. Text representation: flowchart LR Client --> Chi Chi --> Huma Huma --> Command Command --> Domain Domain --> SQL Command --> Outbox Outbox --> Publisher
Diagram source
flowchart LR
  Client --> Chi
  Chi --> Huma
  Huma --> Command
  Command --> Domain
  Domain --> SQL
  Command --> Outbox
  Outbox --> Publisher
  1. Handlers call command.Domain1 or command.Domain2.
  2. Domain packages (internal/domains/domain1) insert into domain1 and append to domain1_events.
  3. The publisher drains each domain outbox to Azure Service Bus (emulator locally).

Logging

Middleware order is RequestID → tracing → RequestLog → Recover → request metadata. The API starts the optional exporter in serving mode, and the listener runners start it for message processing. The outbox and shared Service Bus transport preserve trace context. See HTTP and messaging tracing. RequestLog wraps Recover so a panic has already become a 500 by the time the request is logged.

Each finished request produces one line — method, path, route, status, durationMs, requestId, and command when the request matched an operation — and the response carries X-Request-Id and X-Response-Time-Ms. Health probes and the Swagger UI assets are skipped. serviceDomain narrows to the matched domain, so a POST /v1/domain1 logs as template-domain1.

Format follows APP_ENV: local gets the console handler, anything else gets JSON. Both go to stderr, because cmd/api -openapi owns stdout.

[16:57:09.049] INFO (template-domain1): POST /v1/domain1 201 (12.4 ms)
    serviceDomain: "template-domain1"
    env: "local"
    startupPath: "services/template/cmd/api"
    requestId: "0d9b1f1e-6c2f-47ab-9d0a-2f1c0f5e9b31"
    status: 201
    durationMs: 12.4
    command: "domain1.create"

requestId is written into the outbox row's headers inside the producing transaction, published as the broker correlation id, and logged again by the listeners — so one search covers the request and every event it caused. See ADR 0017.

Domains

DomainTableOutboxRoutes
domain1domain1domain1_eventsPOST/GET /v1/domain1, GET /v1/domain1/{id}
domain2domain2domain2_eventsPOST/GET /v1/domain2, GET /v1/domain2/{id}

Event payloads: services/template/domains/domain1/events/ and services/template/domains/domain2/events/.

Business logic: services/template/internal/domains/domain1/create.go.

Each sample domain has its own fanout and a list of subscribers in services/template/.azure/bus/topology.yaml. See Operations.

Tests

go test ./services/template/...

On this page