Project record
03 / 03Explainable travel recommendation engine
Viazio
It picks when and where to travel by weighing holidays, weather, cost, and distance, and shows why each destination landed in that position instead of handing over a single score. When an upstream source goes down, the ranking stays standing and says what was missing.
Viazio compares travel windows and destinations through weather, cost, distance, and calendars. Its response separates score, coverage, and confidence so it remains useful when a provider fails without presenting missing data as observed data.
- Period
- 03.2026 · 07.2026
- My contribution
- Recommendation-engine architecture, external integrations, persistence, Spring Boot API, and React frontend.
- Technology
- Java 21 · Spring Boot · React 19 · PostgreSQL · Resilience4j
- Status
- Published version
RING
Ranking destinations with incomplete data
Weather, exchange rates, holidays, economic indicators, and editorial content have different lifetimes and failure modes. Waiting for every source makes ranking slow; replacing missing data with zero punishes a destination for a provider failure.
SECTOR
How the ranking degrades
Score and coverage remain separate
Four strategies calculate weather, cost, distance, and festivities. When a criterion is missing, available weights are normalized and coverage lowers final confidence without inventing a score.
Failures stay isolated by provider
Retries, circuit breakers, and bulkheads keep independent state. A Wikipedia outage removes editorial content without opening the weather, holiday, or World Bank circuits.
PostgreSQL before the network
Reads use persisted data with fetched_at, stale_at, and expires_at. A miss gets at most two seconds of synchronous work; after that, the response degrades and a refresh enters the queue.
Bounded concurrency per candidate
Candidates are evaluated on virtual threads, while the refresh queue uses priorities and ShedLock. One candidate's failure does not stop the remaining ranking.
MARK
Executable evidence
- 250 backend tests and 17 frontend unit tests
- Five remote providers with separate cache, protection, and failure behavior
- A persisted refresh queue for missing, expired, or low-confidence data
- A public application with no account requirement, a versioned API, and an open repository
SHEET