Data Flow
How data moves from persistent storage to the screen in BodyMetrics.
Overview
Data in BodyMetrics flows in one direction: from persistent storage through use cases and the domain facade to the view, which builds a render model passed to a renderer. No layer reads backwards from a lower layer.
Flow Diagram
┌─────────────────────────────────────────────────────────────────┐
│ Persistent Storage │
│ (DataProvider · History · Targets · GarminProfile) │
└──────────────────────────┬──────────────────────────────────────┘
│ read / write
┌──────────────────────────▼──────────────────────────────────────┐
│ Use Cases │
│ (Measurements · Profile · Targets · Trend · ResetUserData) │
└──────────────────────────┬──────────────────────────────────────┘
│ view-ready data
┌──────────────────────────▼──────────────────────────────────────┐
│ Domain Facade (BodyMetricsDomain) │
│ + Policies (Classification · Thresholds · HealthCalculators) │
│ + Trend Cache (TrendCacheService) │
│ + Locale (BodyMetricsLocale) │
└──────────────────────────┬──────────────────────────────────────┘
│ view model / callbacks
┌──────────────────────────▼──────────────────────────────────────┐
│ View (BodyMetricsView) │
│ Builds render model dictionary │
└──────────────────────────┬──────────────────────────────────────┘
│ {:metric, :value, :zone, …}
┌──────────────────────────▼──────────────────────────────────────┐
│ Renderers │
│ Draw to Garmin Graphics.Dc │
└─────────────────────────────────────────────────────────────────┘
Main App Launch Flow
BodyMetricsApp.onStart()initialises the domain and creates the view.- The view checks whether a profile exists via the domain.
- No profile → enters setup wizard mode immediately.
- Profile exists → enters summary mode.
- The view calls
onUpdate(dc)on every display refresh, dispatching to the correct renderer based on the current mode.
Metric Navigation Flow
- User presses UP / DOWN on the Summary screen.
BodyMetricsInputDelegatecallsview.nextMetric()/view.prevMetric().- The view updates
_selectedMetricand requests a redraw. onUpdate()builds a new render model from the domain and callssummaryDetailRenderer.drawSummary(dc, model).
Measurement Entry Flow
- User opens Check-ins wizard via menu.
- View enters
MODE_DATA_ENTRY. - UP/DOWN cycle through the bounded field range (from
MeasurementsUseCase.fieldStep()). - On ENTER, the view advances to the next field.
- After the last field, the view calls
domain.saveMeasurements(values). - The use case writes to
DataProvider, records a history snapshot inHistory, and invalidates the trend cache. - The view returns to summary mode.
Target Derivation Flow
When displaying the Target Delta screen, BodyMetrics resolves the effective target:
Has user custom target?
YES → use custom target value
NO → call ThresholdFactory.effectiveTarget(metric, profile)
→ returns policy-derived target
The delta is currentValue − effectiveTarget, formatted and coloured by ClassificationPolicy.
Trend Cache Flow
- History is loaded from persistent storage by
TrendUseCase. TrendCacheServicepre-processes and caches windows for fast rendering.- Cache is invalidated when:
- a measurement is saved;
- a full reset is performed;
- debug history actions are triggered;
- the selected metric changes;
- the trend window changes.
- On cache miss, the service recomputes from raw history.
Garmin Weight Merge
ProfileUseCase reads the Garmin UserProfile weight (if available) and compares it with the locally stored weight. The merge rule is:
- If Garmin weight is present and the user has not overridden it manually, use the Garmin value.
- If the user has entered a manual weight, the manual value takes precedence.
See Also
- Architecture — the full layer diagram.
- Rendering System — how the render model is consumed.
- i18n System — how localized strings are resolved.