Reines HTML5/CSS3/JavaScript ohne Build-Tooling, lauffaehig direkt per Doppelklick auf index.html (file://, kein Server noetig): - Datenmodell fuer Ausflugsziele mit allen Feldern aus der Anforderung, 15 feste Kategorien inkl. Farbe/Icon - Zentraler Pub/Sub-Store mit LocalStorage-Persistenz und Migrations-Grundgeruest - Interaktive Karte (Leaflet + Leaflet.markercluster, lokal vendored) mit kategoriefarbenen Markern, Clustering, Popups, Geolocation-Button - Kartengrundlage ueber CARTO-Basemaps statt tile.openstreetmap.org, da dessen Referer-Pflicht file://-Aufrufe blockiert - Seed-Daten als eingebettetes Script (data/seed.js) statt JSON-Datei, da fetch() auf lokale Dateien unter file:// nicht zuverlaessig funktioniert - Ergebnisliste mit 10 Beispiel-Ausflugszielen, sicher gerendert ueber textContent/DOM-APIs statt innerHTML (Vorbereitung fuer spaeteren KI-JSON-Import mit Fremddaten) - Responsives Layout (Desktop nebeneinander, Mobile gestapelt)
99 lines
5.3 KiB
Markdown
99 lines
5.3 KiB
Markdown
# ADR 0001: Kein Build-Tooling, klassische Scripts mit globalem Namespace, Leaflet lokal vendored
|
||
|
||
## Status
|
||
|
||
Akzeptiert (überarbeitet – siehe "Revision" unten; ursprüngliche
|
||
Entscheidung für native ES-Module wurde revidiert)
|
||
|
||
## Kontext
|
||
|
||
`anforderung.md` fordert explizit "möglichst einfache und gut verständliche
|
||
Technologien" (HTML5, CSS3, JavaScript, eine Open-Source-Kartenbibliothek,
|
||
LocalStorage) und stellt keinen Backend-Zwang für die erste Version. Die
|
||
Anwendung soll außerdem vollständig responsiv sein und ohne größere
|
||
Infrastruktur betrieben und gewartet werden können.
|
||
|
||
Die tatsächliche Zielumgebung des Nutzers wurde präzisiert: Die App wird
|
||
per **Doppelklick auf `index.html`** direkt aus einem lokal synchronisierten
|
||
Nextcloud-Ordner geöffnet (`file://...`). Es steht **kein lokaler
|
||
HTTP-Server** zur Verfügung und ein solcher soll vom Endnutzer auch nicht
|
||
manuell gestartet werden müssen – Priorität hat maximale Einfachheit für den
|
||
Endnutzer, nicht Entwicklerkomfort.
|
||
|
||
## Revision (2026-07-24)
|
||
|
||
Die ursprüngliche Entscheidung, JavaScript als native ES-Module
|
||
(`<script type="module">` mit `import`/`export`) umzusetzen, war für diesen
|
||
Anwendungsfall **falsch**: Browser blockieren das Nachladen von ES-Modulen
|
||
über `file://` per CORS-Policy (`Access to script ... has been blocked by
|
||
CORS policy`). Die App ließ sich damit nur über einen lokalen HTTP-Server
|
||
starten – was der Anforderung "Doppelklick, kein Server" widerspricht.
|
||
|
||
**Neue Entscheidung:** Alle JavaScript-Dateien wurden von ES-Modulen auf
|
||
**klassische, nicht-modulare `<script>`-Tags** umgebaut. Statt
|
||
`import`/`export` hängt jede Datei ihre öffentliche API an ein einziges
|
||
globales Namespace-Objekt `window.EventMap` (mit Sub-Namespaces `utils`,
|
||
`config`, `models`, `store`, `map`, `ui`) an. Jede Datei kapselt ihre
|
||
internen Hilfsvariablen weiterhin in einer IIFE
|
||
(`(function () { ... })();`), sodass nur das explizit an `EventMap.*`
|
||
angehängte gemeinsam genutzt wird. Die Ladereihenfolge in `index.html`
|
||
(Utils → Config → Models → Store → Map → UI → `main.js`) stellt sicher,
|
||
dass jede Datei beim Ausführen bereits auf die Namespaces ihrer
|
||
Abhängigkeiten zugreifen kann.
|
||
|
||
## Entscheidung
|
||
|
||
- Es wird **kein Build-Tooling** (kein Bundler wie Webpack/Vite, kein
|
||
Transpiler, kein Package-Manager-Setup für die App selbst) eingesetzt.
|
||
- JavaScript wird als **klassische Scripts** (`<script src="...">`, ohne
|
||
`type="module"`) eingebunden, organisiert über einen gemeinsamen globalen
|
||
Namespace `window.EventMap` statt über `import`/`export`. Jede Datei ist
|
||
in eine IIFE gekapselt.
|
||
- **Leaflet** und **Leaflet.markercluster** werden **lokal vendored** unter
|
||
`vendor/leaflet/` bzw. `vendor/leaflet.markercluster/` (JS, CSS und
|
||
Marker-Bildassets), nicht per CDN eingebunden.
|
||
|
||
## Begründung
|
||
|
||
- Entspricht direkt der Anforderung nach einfachen, gut verständlichen
|
||
Technologien ohne zusätzliche Lernkurve (kein Framework, kein Build-Schritt).
|
||
- Ermöglicht das Öffnen der App per Doppelklick aus dem synchronisierten
|
||
Ordner heraus, ohne dass der Endnutzer einen Server starten oder
|
||
irgendetwas installieren muss – das ist für die Zielgruppe wichtiger als
|
||
natives Modul-Tooling.
|
||
- Lokal vendorte Bibliotheken machen die App **offline-fähig** und
|
||
unabhängig von der Verfügbarkeit externer CDNs; das passt zum
|
||
Offline-First-Ansatz der LocalStorage-Datenhaltung.
|
||
- Geringere Angriffsfläche/Komplexität: keine Abhängigkeit von Node-Toolchain-
|
||
Versionen, keine Build-Artefakte, die aktuell gehalten werden müssten.
|
||
|
||
## Konsequenzen
|
||
|
||
- **Trade-off gegenüber ES-Modulen:** Es gibt kein natives Modul-Scoping
|
||
mehr. Stattdessen dient der globale `window.EventMap`-Namespace plus
|
||
IIFE-Kapselung pro Datei als Ersatz. Das erfordert Disziplin beim
|
||
Vergeben von Namen unter `EventMap.*` (keine versehentlichen
|
||
Überschreibungen) und macht Abhängigkeiten weniger explizit als
|
||
`import`-Statements – sie ergeben sich aus der Script-Reihenfolge in
|
||
`index.html`, die entsprechend sorgfältig gepflegt werden muss.
|
||
- **Behobenes Restrisiko:** Ursprünglich lud `js/store/store.js` die
|
||
Beispieldaten per `fetch('data/seed.json')`, was unter `file://`
|
||
(insbesondere in Chrome/Chromium) von der Same-Origin-Policy blockiert
|
||
wird. Die Seed-Daten wurden daher von `data/seed.json` nach
|
||
`data/seed.js` verschoben und liegen dort als eingebettetes
|
||
`<script>`-Objekt (`EventMap.data.SEED_PLACES`), das wie jede andere
|
||
JS-Datei klassisch eingebunden wird. `initStore()` ist dadurch wieder
|
||
synchron; `fetch()` kommt aktuell an keiner Stelle der Anwendung mehr
|
||
vor. Für spätere Phasen (z.B. KI-JSON-Import) ist zu beachten: Datei-Uploads
|
||
über `<input type="file">` + `FileReader` sind unproblematisch unter
|
||
`file://`, ein `fetch()` auf lokale Dateien dagegen nicht.
|
||
- Es gibt keinen automatischen Minify-/Tree-Shaking-Schritt; die
|
||
Dateigröße im Produktivbetrieb ist etwas größer als mit Bundler, was für
|
||
den Umfang dieser Anwendung akzeptabel ist.
|
||
- Updates der vendorten Bibliotheken müssen manuell durchgeführt werden
|
||
(Dateien in `vendor/` austauschen), es gibt keinen automatisierten
|
||
Dependency-Update-Mechanismus (z.B. Dependabot) für diese Assets.
|
||
- Sollte die Anwendung später deutlich wachsen (z.B. TypeScript, größere
|
||
Testinfrastruktur, Bereitstellung über einen echten Webserver), kann
|
||
diese Entscheidung revidiert und zurück auf ES-Module umgestellt werden.
|