Add internal architecture documentation
This commit is contained in:
143
docs/internal/presenters.md
Normal file
143
docs/internal/presenters.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# Presenters and Templates
|
||||
|
||||
This document describes payload shaping in
|
||||
`internal/adapters/inbound/httpapi/presenter` and its relationship to text
|
||||
templates. Public response fields are documented in [`docs/api.md`](../api.md).
|
||||
|
||||
## Purpose
|
||||
|
||||
Presenters translate repository/application read models into response-ready
|
||||
payloads. They own unit conversion, numeric rounding, timezone conversion,
|
||||
optional field preservation, and copy semantics.
|
||||
|
||||
## Inputs and Outputs
|
||||
|
||||
Inputs:
|
||||
|
||||
- weatherfeeder model values returned by the repository;
|
||||
- `app.CurrentConditions` values returned by the application service;
|
||||
- requested unit mode;
|
||||
- requested precision;
|
||||
- optional timezone location.
|
||||
|
||||
Outputs:
|
||||
|
||||
- JSON/XML/text-ready payload structs or canonical model copies;
|
||||
- nil payloads for nil inputs.
|
||||
|
||||
## Boundaries
|
||||
|
||||
Presenters may:
|
||||
|
||||
- convert metric values to US-customary response fields;
|
||||
- round numeric values that are presentation values;
|
||||
- convert timestamps to a requested timezone;
|
||||
- copy slices and pointers before changing presentation values;
|
||||
- add template-only helper fields when they are not serialized.
|
||||
|
||||
Presenters must not:
|
||||
|
||||
- execute SQL;
|
||||
- call services or repositories;
|
||||
- parse HTTP query parameters;
|
||||
- load templates;
|
||||
- change canonical repository models in place.
|
||||
|
||||
## Config Fields Used
|
||||
|
||||
Presenters do not read config directly. They receive unit, precision, and
|
||||
timezone selections from HTTP binders. Text output depends indirectly on
|
||||
`templates.base_dir` because feedapi loads templates from that configured
|
||||
directory.
|
||||
|
||||
## External Adapters Used
|
||||
|
||||
- `weatherfeeder/model` for canonical weather payloads.
|
||||
- `weatherfeeder/standards` for current-condition text from WMO codes.
|
||||
- `internal/app` for the current-conditions read model.
|
||||
- Feedapi templates indirectly consume presenter output during text rendering.
|
||||
|
||||
## State
|
||||
|
||||
Presenters are stateless. Helper functions allocate copied values and return
|
||||
new payloads for each request.
|
||||
|
||||
## Unit Conversion
|
||||
|
||||
Metric mode generally returns weatherfeeder canonical model shapes or
|
||||
metric-named fields. US mode uses explicit US response structs for observations
|
||||
and forecasts and US-specific fields in current conditions.
|
||||
|
||||
Conversion constants live in `constants.go`:
|
||||
|
||||
- Celsius to Fahrenheit;
|
||||
- kilometers per hour to miles per hour;
|
||||
- meters to miles or feet;
|
||||
- pascals to inches of mercury;
|
||||
- millimeters to inches.
|
||||
|
||||
Non-unit fields such as percentages, directions, text, IDs, and ordering values
|
||||
keep their existing values.
|
||||
|
||||
## Precision
|
||||
|
||||
`roundedPtr` and `roundFloat` round numeric presentation values. Precision `0`
|
||||
rounds to whole numbers. Positive precision uses powers of ten. Nil numeric
|
||||
pointers remain nil.
|
||||
|
||||
Latitude and longitude are copied but not rounded by forecast presenters.
|
||||
|
||||
## Timezone Conversion
|
||||
|
||||
`inLocationTime` and `inLocationTimePtr` convert timestamps only when a
|
||||
timezone is supplied. Without a timezone, timestamps are preserved as returned
|
||||
by the repository.
|
||||
|
||||
Timezone conversion is applied by forecast, discussion, and weather story
|
||||
presenters. Observations and current conditions do not currently receive
|
||||
timezone input from their routes.
|
||||
|
||||
## Optional Fields and Copy Semantics
|
||||
|
||||
Presenter helpers copy pointer values before changing them. Slices are copied
|
||||
before inclusion where needed. This preserves nil/omitempty behavior and avoids
|
||||
mutating repository-returned values.
|
||||
|
||||
Nil input payloads return nil. Endpoint handlers wrap those nil payloads in a
|
||||
response envelope so renderers can produce `data: null`.
|
||||
|
||||
## Endpoint Families
|
||||
|
||||
- Observations: metric copy or US response shape, including present-weather
|
||||
slice copy.
|
||||
- Current conditions: single response shape with either metric or US unit
|
||||
fields populated, plus a template-only day/night text helper.
|
||||
- Alerts: pass-through of canonical alert runs, with nil preserved.
|
||||
- Forecasts: metric copy or US response shape, period copy, unit conversion,
|
||||
precision, and timezone conversion.
|
||||
- Discussions: full or focused payload shapes, section copy, key-message copy,
|
||||
timezone conversion.
|
||||
- Weather stories: run/story copy and timezone conversion.
|
||||
|
||||
## Templates
|
||||
|
||||
Text templates consume the same envelope data produced by presenters. Template
|
||||
files live under `templates/` and are loaded by feedapi from `templates.base_dir`.
|
||||
|
||||
Presenter changes can break text output even when JSON and XML still compile.
|
||||
Check template field references before renaming or removing presenter fields.
|
||||
|
||||
## Tests to Inspect Before Changing
|
||||
|
||||
- `internal/adapters/inbound/httpapi/presenter/payload_test.go`
|
||||
- `internal/adapters/inbound/httpapi/endpoints_test.go` for rendered text and
|
||||
envelope behavior
|
||||
- affected template files under `templates/`
|
||||
|
||||
## Invariants
|
||||
|
||||
- Keep unit conversion and rounding out of handlers and repositories.
|
||||
- Preserve nil pointer and optional field behavior.
|
||||
- Copy before converting, rounding, or timezone-shifting values.
|
||||
- Keep text-template helper fields out of JSON/XML when they are not API fields.
|
||||
- Update `docs/api.md` and templates when payload shape changes.
|
||||
Reference in New Issue
Block a user