Add internal architecture documentation
This commit is contained in:
151
docs/internal/http-adapter.md
Normal file
151
docs/internal/http-adapter.md
Normal file
@@ -0,0 +1,151 @@
|
||||
# HTTP Adapter
|
||||
|
||||
This document describes the internal HTTP adapter under
|
||||
`internal/adapters/inbound/httpapi`. The public endpoint contract belongs in
|
||||
[`docs/api.md`](../api.md).
|
||||
|
||||
## Purpose
|
||||
|
||||
The HTTP adapter turns feedapi route definitions into calls on the application
|
||||
service boundary. It owns route registration, query binding, request validation,
|
||||
forecast day-slice filtering, response envelopes, and template names.
|
||||
|
||||
## Inputs and Outputs
|
||||
|
||||
Inputs:
|
||||
|
||||
- feedapi HTTP requests;
|
||||
- query parameters;
|
||||
- an implementation of the adapter-local `Service` interface.
|
||||
|
||||
Outputs:
|
||||
|
||||
- `endpoint.Definition` values registered by runtime composition;
|
||||
- `response.Envelope{Data: ...}` values for successful requests;
|
||||
- typed feedapi errors for invalid query parameters;
|
||||
- endpoint-specific template names for text rendering.
|
||||
|
||||
## Boundaries
|
||||
|
||||
The adapter may:
|
||||
|
||||
- define routes and supported response formats;
|
||||
- bind and validate query parameters;
|
||||
- call the `Service` interface;
|
||||
- choose the presenter function for an endpoint;
|
||||
- filter forecast copies for `/today` and `/tomorrow`.
|
||||
|
||||
The adapter must not:
|
||||
|
||||
- execute SQL;
|
||||
- open databases;
|
||||
- mutate repository-returned models in place;
|
||||
- duplicate public API reference text;
|
||||
- move unit conversion or timezone presentation out of presenters.
|
||||
|
||||
## Config Fields Used
|
||||
|
||||
The HTTP adapter does not read config directly. Feedapi applies server and
|
||||
renderer configuration before requests reach these handlers. Template names are
|
||||
declared in endpoint definitions, but `templates.base_dir` is loaded by feedapi.
|
||||
|
||||
## External Adapters Used
|
||||
|
||||
- `feedapi/endpoint` for route definitions.
|
||||
- `feedapi/render` for declared output formats.
|
||||
- `feedapi/response` for success envelopes.
|
||||
- `feedapi/bind` and `feedapi/errors` for query validation failures.
|
||||
- `presenter` package for payload shaping.
|
||||
|
||||
## State
|
||||
|
||||
The adapter has no durable state. `forecastNow` is package-level state only to
|
||||
make forecast day filtering testable. Do not add request caches or cross-request
|
||||
mutable state here.
|
||||
|
||||
## Route Registry
|
||||
|
||||
`Definitions(svc)` returns all implemented route definitions. It includes:
|
||||
|
||||
- observations;
|
||||
- active alerts;
|
||||
- current conditions;
|
||||
- weather stories;
|
||||
- forecast discussions;
|
||||
- hourly and narrative forecasts.
|
||||
|
||||
Each route uses `endpoint.GET`, declares JSON/XML/text production, and names a
|
||||
text template. Feedapi owns routing, format negotiation, response rendering,
|
||||
middleware, and error normalization after definitions are registered.
|
||||
|
||||
## Query Binding
|
||||
|
||||
There are three binder shapes:
|
||||
|
||||
- `bindQuery`: `format` and `units`;
|
||||
- `bindPrecisionQuery`: `format`, `units`, and `precision`;
|
||||
- `bindForecastPrecisionQuery`: `format`, `units`, `precision`, and timezone;
|
||||
- `bindTimezoneQuery`: `format`, `units`, and timezone.
|
||||
|
||||
All binders use feedapi binding helpers with `RejectUnknown: true`. Supported
|
||||
common query values are lowercased and trimmed before binding where applicable.
|
||||
|
||||
`precision` defaults to `0` and must be between `0` and `2`. Timezone parsing is
|
||||
available only through binders used by forecast, discussion, and weather story
|
||||
routes.
|
||||
|
||||
## Timezone Parsing
|
||||
|
||||
Timezone parsing accepts:
|
||||
|
||||
- IANA names through `time.LoadLocation`;
|
||||
- configured aliases such as `Chicago` and `Stl`;
|
||||
- common US abbreviations handled as fixed zones;
|
||||
- signed UTC offsets.
|
||||
|
||||
If both `tz` and `TZ` are provided, they must match case-insensitively.
|
||||
Invalid timezone input is converted to a feedapi invalid-parameter error.
|
||||
|
||||
## Forecast Day Slices
|
||||
|
||||
Forecast base routes return the latest run unchanged except for presentation.
|
||||
`/today` and `/tomorrow` routes call `filterForecastRunByDaySlice`.
|
||||
|
||||
Filtering behavior:
|
||||
|
||||
- nil runs remain nil;
|
||||
- missing timezone means UTC;
|
||||
- day selection is based on `forecastNow()` in the resolved timezone;
|
||||
- periods are included when `period.StartTime` falls on the target local date;
|
||||
- the run is shallow-copied and `Periods` is replaced with the filtered slice.
|
||||
|
||||
The package variable `forecastNow` exists so endpoint tests can make day-slice
|
||||
behavior deterministic.
|
||||
|
||||
## Failure Behavior
|
||||
|
||||
Binder failures become feedapi invalid-parameter responses. Handler service
|
||||
errors are returned unchanged to feedapi for runtime normalization. Nil service
|
||||
payloads are presented as nil data, so response rendering can produce
|
||||
`data: null`.
|
||||
|
||||
## Templates
|
||||
|
||||
Endpoint definitions bind template names only. Template loading and rendering
|
||||
belong to feedapi. Template files live under `templates/` and are operator
|
||||
configuration through `templates.base_dir`.
|
||||
|
||||
## Tests to Inspect Before Changing
|
||||
|
||||
- `internal/adapters/inbound/httpapi/endpoints_test.go`
|
||||
- `internal/adapters/inbound/httpapi/presenter/payload_test.go` when changing
|
||||
presenter interaction
|
||||
- `docs/api.md` when query behavior, route behavior, or payload behavior changes
|
||||
|
||||
## Invariants
|
||||
|
||||
- Preserve strict unknown-query rejection.
|
||||
- Keep route-specific query policy in binder functions.
|
||||
- Keep endpoint handlers thin: bind, call service, present, envelope.
|
||||
- Keep day-slice filtering in the HTTP adapter, not in the repository.
|
||||
- Preserve top-level `data` envelopes and nil-data behavior.
|
||||
Reference in New Issue
Block a user