Documentation
¶
Overview ¶
Command sensor-service is the go-codex flagship example: a small but complete sensor-readings service structured as a real project, with each concern in its own package:
domain/ — Layer 1+2: models, codecs, field factories, constraints,
pure business rules (validated-config factories included)
pipeline/ — business logic: pure mapping functions + the segmented
MQTT pipeline (ports.Chain/ChainStream over boundary
SourcePort/SinkPort + internal PipePort stages),
parameterized by the consumer-defined Store interface
ioports/ — the service's complete IO surface: every boundary
follows the SAME three-step model (declare port shape →
plug in a Pattern → bind an adapter); EventPattern/
SQLPattern/FilePattern/RESTPattern + REST routes
observability/ — cross-cutting CountingObserver (fanned out with a
LoggingObserver, stored once in the context)
adapters/ — infrastructure edge: mock MQTT client, SQL ReadingStore,
HTTP handler factories
db/ — sqlc-generated queries + goose migrations
main.go — wiring ONLY: config, DB, observer, adapter binds, server
demo.go — the runnable demo scenario
Import direction is strictly acyclic: main → {ioports, pipeline, adapters, observability, domain}; pipeline → domain; ioports → domain; adapters → domain; domain → nothing internal but db.
What it demonstrates ¶
- ports.SourcePort + ports.SourcePort.PluginEventPattern + adaptermqtt.SubscribeAdapter — MQTT ingestion wired to the pipeline's first stage (ioports.Sensors); pipeline code has no MQTT import. The topic + params are declared once as a standalone value (ioports.SensorsPattern), plugged in at wiring time — PluginEventPattern registers it AND returns the typed handle in one call, no separate events.NewChannel/Register step needed.
- ports.SinkPort + ports.SinkPort.PluginEventPattern + adaptermqtt.PublishAdapter — MQTT alert publishing wired to the pipeline's LAST stage (ioports.Alerts); supports fan-out to additional sinks via additional Bind calls.
- ports.Chain + ports.ChainStream — pipeline.Build segments the MQTT pipeline into named stages (Sensors → Params → Saved → Alerts) using the SAME call shape whether the endpoint is a boundary SourcePort/ SinkPort or an internal PipePort. Persistence is its OWN Params→Saved edge — ports.IOPort + sqladapter.QueryEachAdapter (ioports.Readings): the pipeline's mapping function stays PURE (payload → insert params); the save happens through the port, whose adapter is chosen here, INSIDE that edge's transform — never buried inside a bigger, multi-purpose stage. Table/Op metadata declared once via ports.SQLPattern, plugged in via ports.NewSQLPort (ioports.Readings/ History/ExportQuery are all declared this way — a thin convenience constructor combining "declare port" + "plug in SQLPattern").
- ports.PipelineSpec — the MQTT pipeline's shape (pipe/port names, buffer sizes, bound adapter identities, every Chain/ChainStream edge with its transform's real Go function identity) derived directly from the four stages, printed in demo.go — no hand-typed topology to keep in sync.
- ports.ToolPort + nethttp.PipelineAdapter — GET /sensors/{sensorID}/readings (ioports.HistoryTool, declared via ports.NewRestToolPort): the tool pipeline Connects through ioports.History (IOPort, SQLPattern) — REST layer and database never meet directly.
- ports.SinkPort + fileadapter.DrainWriteFileAdapter — POST /export (ioports.ExportTool): query through ioports.ExportQuery (SQLPattern), write the snapshot through ioports.NewExportsPort's ports.FilePattern ({exportID}.json); the response path comes from the SAME declaration via File.BuildPath.
- nethttp.HandlerLatest — reactive cache endpoint; GET /readings/latest returns the most recently saved reading without querying the DB.
- Validated-config factory pattern — main() loads domain.AlertConfig once via config.FromEnv (APP_ALERT_THRESHOLD, default 50.0); the pipeline functions close over the typed, validated config (see domain.NewShouldAlert).
- One stats.NewFanout observer across HTTP, MQTT, SQL, file, and stream.
Run:
go run ./examples/sensor-service APP_ALERT_THRESHOLD=90 go run ./examples/sensor-service
Directories
¶
| Path | Synopsis |
|---|---|
|
Package adapters holds the sensor service's infrastructure edge: the mock MQTT client used for the demo, the SQL-backed ReadingStore, and the HTTP handler factories.
|
Package adapters holds the sensor service's infrastructure edge: the mock MQTT client used for the demo, the SQL-backed ReadingStore, and the HTTP handler factories. |
|
Package domain is Layer 1 + Layer 2 of the sensor service: models, codecs, and pure business rules.
|
Package domain is Layer 1 + Layer 2 of the sensor service: models, codecs, and pure business rules. |
|
Package ioports declares every IO boundary of the sensor service as a protocol-agnostic port or route — the service's complete IO surface, readable as a compact spec, with ZERO adapter imports.
|
Package ioports declares every IO boundary of the sensor service as a protocol-agnostic port or route — the service's complete IO surface, readable as a compact spec, with ZERO adapter imports. |
|
Package observability holds the cross-cutting observer for the sensor service.
|
Package observability holds the cross-cutting observer for the sensor service. |
|
Package pipeline is the sensor service's business logic layer: pure mapping functions and the stream topology.
|
Package pipeline is the sensor service's business logic layer: pure mapping functions and the stream topology. |
Click to show internal directories.
Click to hide internal directories.