ADR-026: Multi-Source Data Integration
- Status: Accepted — partially superseded by ADR-044
- Date: 2026-04-18
- Depends on: ADR-005 (External API caching), ADR-012 (Alert engine), ADR-013 (Accommodation discovery), ADR-022 (Persistent storage)
- Extends: ADR-013 (adds DataTourisme and Wikidata as complementary sources)
- Superseded (in part) by: ADR-044 — the data.gouv.fr weekly-markets source (the
markettable, theapp:markets:importcommand, the'market'event type, and the data.gouv.fr attribution) has been removed. The data.gouv.fr resource went dead with no reliable national equivalent, and a market is not actionable without its trading day. The remaining sources (OpenStreetMap, DataTourisme, Wikidata) are unaffected; the passages below describing markets are retained for historical context only.
Context and Problem Statement
OpenStreetMap provides a reliable baseline for geographic data (roads, bike infrastructure, water points, basic POIs). However, several categories of information are systematically under-represented in OSM for itinerant cyclists in France:
| Gap | OSM limitation |
|---|---|
| Bikepacker-friendly accommodation | Gîtes d'étape and auberges routières rarely carry backpack=yes or structured bike tags in OSM |
| Cultural POIs without opening hours | Many châteaux, abbeys, and museums are mapped but lack opening_hours, fee, or multilingual descriptions |
| Dated events | OSM does not model time-bound events (festivals, exhibitions, fairs) |
Two open data sources are available to address these gaps without proprietary API dependencies:
- DataTourisme — the French national tourism data aggregator (Ministry of Tourism), covering accommodations, cultural POIs, and dated events with structured JSON-LD. Published under Licence Ouverte 2.0 (Etalab). Available via a free-registration REST API.
- Wikidata — the structured knowledge base of the Wikimedia Foundation. Q-ID references appear on OSM objects (
wikidata=Q12345) and in DataTourisme payloads (owl:sameAs). Published under CC0. No registration required.
Decision Drivers
- Coverage — Dated events cannot be sourced from OSM alone.
- Legal compliance — All sources must be open-licensed and permit attribution-free or low-burden attribution.
- Operational cost — Sources must be either free or offer sufficient quota for the application's usage pattern.
- Architecture consistency — New sources must plug into the existing alert and enrichment pipelines without requiring a global refactor.
- Graceful degradation — The application must remain fully functional when any optional source is unavailable or unconfigured.
Considered Options
Option A: Scrape RandoCamping.fr
Parse HTML from RandoCamping.fr to extract bikepacker-oriented accommodation listings.
Rejected. RandoCamping's terms of service explicitly prohibit automated scraping. Blocked by anti-bot protections (Cloudflare). Technically fragile to DOM changes. Legally untenable.
Option B: OSM only
Restrict all data to OpenStreetMap. Accept the gaps as known limitations.
Rejected. This option leaves the "dated events" use case entirely unaddressed — OSM does not model events. The accommodation gap means bikepackers will miss gîtes d'étape that are the most common overnight stop in France.
Option C: Duplicate DataTourisme auth per scanner
Add DataTourisme credentials to each scanner class that needs POI or accommodation data, creating N independent HTTP clients.
Rejected. Violates DRY. Rate limiting (1 000 req/h) must be enforced at a single point. Auth rotation or key changes would require N code modifications.
Option D: Multi-source architecture with interface registries and a single DataTourisme client (chosen)
Introduce AccommodationSourceInterface and CulturalPoiSourceInterface to abstract data origin from consumers. Implement OSM and DataTourisme sources behind each interface, auto-discovered via #[AutowireIterator]. A single DataTourismeClient handles auth, rate limiting, and caching for all DataTourisme consumers. Wikidata enrichment runs as a cross-cutting batch pass after primary source data is collected.
Decision Outcome
Chosen: Option D — multi-source architecture with interface registries.
Source roles
| Source | Role | Coverage | Licence | Prerequisite |
|---|---|---|---|---|
| OpenStreetMap | Primary source for all geographic data, bike infrastructure, water points, bike shops, resupply POIs | Global | ODbL | None |
| DataTourisme | Complementary source for accommodations and cultural POIs; exclusive source for dated events (festivals, exhibitions, fairs) | France | Licence Ouverte 2.0 | DATATOURISME_API_KEY |
| Wikidata | Cross-cutting enricher: adds multilingual descriptions, images, Wikipedia links, and structured opening hours to any object carrying a Q-ID | Europe | CC0 | None (optional WIKIDATA_USER_AGENT) |
Architecture
AccommodationSourceInterface CulturalPoiSourceInterface
├── OsmAccommodationSource ├── OsmCulturalPoiSource
└── DataTourismeAccommodationSource └── DataTourismeCulturalPoiSource
│ │
└──────────────┬────────────────────────┘
│
DataTourismeClient
(single instance, rate-limited, Redis-cached)
│
WikidataEnricher ← batch Q-ID resolution after primary collection
Registry pattern: each interface is consumed via #[AutowireIterator] — new sources implement the interface and are discovered automatically without modifying existing consumers.
DataTourisme client (DataTourismeClientInterface): single HTTP client scoped to datatourisme.fr, rate-limited at 1 000 req/h via Symfony Rate Limiter (fixed_window policy), responses cached in a dedicated cache.datatourisme Redis pool (TTL 24h).
Wikidata enricher (WikidataEnricherInterface): batch SPARQL queries via the public Wikidata endpoint. Results cached in cache.wikidata Redis pool (TTL 7 days). Errors (timeout, 5xx) are silently swallowed — the application continues without enrichment.
Consequences
Positive
- Dated events now supported — The first alert rule covering cultural/social events around stage endpoints is enabled by DataTourisme.
- Richer accommodation data — Gîtes d'étape and accommodation types absent from OSM are now discoverable.
- Multilingual enrichment — Wikidata Q-IDs unlock descriptions, images, and Wikipedia links in FR/EN/DE/ES/IT without per-source effort.
- Interface abstraction — Adding a new source (e.g., regional tourism APIs) requires only a new class implementing the relevant interface.
Negative
- New Redis pools —
cache.datatourismeandcache.wikidataadd two named pools to the Redis configuration. Memory quota monitoring is required. - DataTourisme quota — 1 000 req/h requires monitoring. A single trip computation may consume up to ~20 requests (one per stage × two queries: events + POIs).
- Multi-source attribution required in the UI — ODbL (OSM), Licence Ouverte 2.0 (DataTourisme), and CC0 (Wikidata) must all be credited in the application footer (see F.4 implementation).
Neutral
- DataTourisme is opt-in:
DATATOURISME_ENABLED=false(the default) skips all DataTourisme queries and falls back to OSM only. The application is fully functional without a DataTourisme API key. - Wikidata is always enabled but degrades silently on errors — it is never a blocking dependency.