ADR-037 — Shared surface-site type, deferred shared <SurfaceMap> component
Status · Accepted (closes RFC-012 OQ-1) Date · 2026-05-09 (back-filled from v0.4.0 implementation) Closes · RFC-012 OQ-1 ("Generic SurfaceMap component, or independent /mars + /moon?") TA anchor · §components/surface-routes Related ADRs · ADR-019 (ajv schema), ADR-038 (per-body 2D projection), ADR-039 (cross-link contract)
Context
PRD-009 specified /mars as a peer of /moon: same panel pattern, same site catalogue shape, same 3D + 2D toggle. RFC-012 OQ-1 asked whether the two routes should share a generic <SurfaceMap> component or stay independent.
Two stages were considered:
- Shared TYPE — pull the
MarsSite+MoonSiteinterfaces out into a singleSurfaceSitediscriminated union with akind: 'surface' | 'orbiter'field. Both routes alias the type so the JSON schema is shared, validate-data covers both with one ruleset, and panel rendering can share helpers. - Shared COMPONENT — pull the actual 3D scene + 2D map + panel-mount logic into a
<SurfaceMap site={…}>component used by both routes.
Decision
Shared TYPE — shipped in v0.4.0
src/types/surface-site.ts defines the canonical SurfaceSite interface plus the kind: 'surface' | 'orbiter' discriminator. src/types/mars-site.ts and src/types/moon-site.ts both export { SurfaceSite as MarsSite } / MoonSite for ergonomic per-route imports. Shared schema in static/data/schemas/surface-site.schema.json validates both mars-sites.json and moon-sites.json.
This gives V1 the data-layer + validation benefits without forcing the routes to share visual code while their 2D projections diverge (see ADR-038).
Shared COMPONENT — deferred to v0.6.0
The shared <SurfaceMap> component refactor is tracked as issue #42 on the v0.6.0 milestone. /mars and /moon currently have independent +page.svelte files that share helpers via src/lib/ (mostly site-state derivations + data fetchers) but not the rendering loop.
Rationale for deferring:
- The Moon's orthographic-pair 2D projection (near + far disc) and Mars's equirectangular projection (single rectangular map) are visually different enough that a single component would need either two render paths or a per-body adaptor — that's the same complexity as keeping two routes.
- /mars shipped with rover traverses (RFC-012 OQ-6) which /moon does not have. Adding traverses to a shared component would increase its API surface.
- The cross-link contract (ADR-039) operates at the data-layer + URL level, not the component level, so it's unaffected by the routing split.
When Apollo / Chandrayaan / Chang'e site overlays land for /moon and the visual pattern stabilises, the shared-component refactor becomes worthwhile. Until then, two +page.svelte files of ~1100 lines each is cheaper than the abstraction.
Rationale
The data-layer abstraction (shared TYPE + schema) gives 80% of the maintenance benefit (one schema, one validator, one set of cross-link helpers) at 20% of the abstraction cost. The component-layer abstraction would force a single rendering path for two routes whose visual designs are still diverging — premature.
Alternatives considered
- Single shared component for both routes — see "deferred" above. Premature.
- No shared type — keeps full per-route freedom but duplicates the JSON schema and the cross-link helpers. Rejected because the data-layer cost is real.
Consequences
Positive:
- One source of truth for surface-site shape across
/marsand/moon. - One schema validates both sites JSON files; ajv catches drift.
- Cross-link helpers (
/mars↔/missions,/moon↔/missions) share a single implementation per ADR-039. - Two route files keep visual freedom for as long as projection / overlay design is moving.
Negative:
- Two
+page.sveltefiles of similar shape — visible duplication that future contributors may try to factor out. - The
<SurfaceMap>shared component is now an outstanding refactor ticket (#42 on v0.6.0).
Implementation notes
src/types/surface-site.ts— canonicalSurfaceSiteinterface + discriminator.src/types/mars-site.ts,src/types/moon-site.ts— re-exports.static/data/schemas/surface-site.schema.json— shared schema.scripts/validate-data.tsvalidates bothmars-sites.jsonandmoon-sites.jsonagainst the same schema.src/routes/mars/+page.svelte,src/routes/moon/+page.svelte— independent route files.- Component-shared refactor tracked as #42 (v0.6.0 milestone).