Replace all TypeScript JSON imports with an HWDataService that loads 6 data files from /assets/files-data/ via HttpClient with shareReplay(1). Update all 9 consumer components to subscribe asynchronously. Remove unused qcm-bpa.json import and dead methods from QcmComponent. Add ADR 0013 documenting the decision.
3.8 KiB
Migrate HeroWars static JSON imports to HttpClient assets
- Status: accepted
- Date: 2026-04-26
Context and Problem Statement
Six large JSON files (36 KB–3 MB) were statically imported directly into Angular components and services via TypeScript import statements. This bundled all the data into the main JavaScript bundle, inflating the initial download regardless of whether the user navigated to the HeroWars section. The largest file (hw-guild-raids.json, 3 MB) alone would significantly delay first contentful paint for all users.
Additionally, a seventh file (qcm-bpa.json, 210 KB) was imported but never consumed — the component had switched to a route resolver but the dead import remained.
Decision Drivers
- Static JSON imports are compiled into the bundle — they cannot be lazy-loaded or cached by the browser independently.
- Updating the data files required a recompile and redeployment; serving them as assets allows hot-swapping without a rebuild.
- The HeroWars section is used by a small subset of users; its data should not penalise all other users' load time.
Considered Options
- Keep static imports
- Move JSON to assets and load via
HttpClient - Fetch data from the API backend
Decision Outcome
Chosen option: "Move JSON to assets and load via HttpClient", because it removes the data from the bundle, enables browser-level HTTP caching, and requires no backend changes. The files are not secret (guild-internal analytics data) so serving them as static assets is appropriate.
Structure:
- JSON files moved from
src/files-data/tosrc/assets/files-data/ HWDataServicecreated atsrc/app/core/services/herowars/hw-data.service.ts— exposes onereadonlyobservable per file, each piped throughshareReplay(1)so the HTTP request fires at most once per app lifecycleHWClanService.loadClan()andHWMemberService.loadMembers()converted from synchronous return values toObservable<T>- All consumer components inject
HWDataServiceand subscribe inngOnInit(); template-called methods that previously accessedguildWarSlots.slotsinline now read from a_slotsclass property populated in the subscribe callback - Unused
import data from 'src/qcm-bpa.json'and two dead import-helper methods removed fromqcm.component.ts
Positive Consequences
- HeroWars JSON (~4 MB total) no longer shipped in the initial bundle.
- Each JSON file is independently cacheable by the browser with standard HTTP cache headers.
- Data files can be updated without recompiling the Angular application.
shareReplay(1)ensures a single HTTP request per session even when multiple components subscribe to the same observable.
Negative Consequences
- Components now initialize asynchronously — there is a brief render before data arrives (consistent with the rest of the app's HTTP-driven components).
Object.entries()onany-typed HTTP responses requires explicitRecord<string, T>casts to satisfy strict TypeScript, adding minor verbosity.
Pros and Cons of the Options
Keep static imports
- Good, because synchronous — no async lifecycle complexity.
- Bad, because adds up to 4 MB to the initial bundle.
- Bad, because data updates require a full rebuild and redeployment.
Move JSON to assets and load via HttpClient
- Good, because removes data from the bundle entirely.
- Good, because browser-cacheable independently of the app JS.
- Good, because hot-swappable without a recompile.
- Bad, because converts synchronous service methods to observables, requiring component updates.
Fetch from API backend
- Good, because data could be managed dynamically via the admin interface.
- Bad, because requires significant backend work (new endpoints, data model) for data that is currently managed as flat files.
- Bad, because adds latency and a failure mode not present with local assets.