Skip to content
version: 1.1.1

ERO Architecture

What was built. Event Route Optimiser is an Astro Progressive Web Application backed by a Cloudflare Worker API, designed so that the interface never waits for the network.

The constraints this design answers are in ERO Requirements. The code that realises it is in ERO Implementation.

1. Application topology

apps/event-route-optimiser/
web/ Astro offline-first PWA, Dexie over IndexedDB
api/ Cloudflare Worker edge API, Hono, bound to D1 SQLite

The browser holds a complete working copy of the data it needs. The Worker is the synchronisation partner and the authority for master schedule data. Azure is not on the request path.

2. Storage model

Two layers, with a strict division of authority.

LayerTechnologyAuthority
EdgeCloudflare D1Authoritative public schedules, backup of user routes
DeviceDexie over IndexedDBActive runtime storage, personal overrides, route calculations

Schemas are kept structurally identical across both layers, so synchronisation is a merge rather than a translation. The schemas are in ERO Reference.

3. Synchronisation engine

Network calls are treated as unreliable side effects. Every user action commits locally first and renders immediately.

[User Action]
│
▼
[Write to Dexie] ──► (Instant UI Render)
│
▼
[Check Connectivity]
├── NO ──► Queue in IndexedDB (sync_status = 'pending')
└── YES ──► POST to Cloudflare Worker API
├── SUCCESS ──► Mark local record 'synced'
└── FAILURE ──► Retain 'pending'; retry on next online event

3.1 Conflict resolution

  • Personal data is client-owned. For selections such as highlighted bands, the local timestamp overwrites the edge record.
  • Master data is read-only. Festival schedules, stage names, and act times originate from D1 and are never modified by a client.
  • Local overrides are protected. When the background worker fetches fresh data, it performs a differential merge. It must never overwrite a row where is_local_override is true.

That last rule is the one that matters most in the field. A user who moves an act to a different stage at 9pm must still see their edit after the next successful sync.

4. Now and Next engine

Real-time logistics are computed entirely on device, so the answer is available with the radio off.

The query takes the current time and reads the local database for acts the user has highlighted. It returns:

  • Now. The act currently playing, where the current time falls between start and end.
  • Next. The next act scheduled.
  • Transition time. The spatial travel time between the Now stage and the Next stage.

Whenever is_local_override is true, the query prefers local_start_time and local_stage_id over the master schedule.

5. Routing engine

Route optimisation runs in the Worker, where the site distance matrix lives. The engine takes the user’s highlighted schedule and applies Dijkstra shortest path combined with weighted interval scheduling to compute travel time between consecutive acts.

Where acts clash, the algorithm penalises the overlap and produces an exact departure time that minimises missed music. Output is a strict itinerary alternating act and transit entries.

6. Offline shell

The application is installable and caches its own shell.

  • The @vite-pwa/astro integration is configured with registerType: 'autoUpdate'.
  • Workbox caches the shell aggressively using globPatterns: ['**/*.{js,css,html,ico,png,svg}'].
  • The Dexie database EventRouteOptimiserDB mirrors the D1 contract with stores for festivals, stages, and bands.

7. Visual system

The interface is dark-native, high-contrast, and built to be read in direct sunlight.

This palette is no longer product-specific. It was promoted to the Synkronyx baseline, so the tokens, contrast measurements, and usage rules now live in Platform Architecture. The source of truth is tokens/synkronyx-palette.json in the brand repository.

ERO consumes those tokens rather than redefining them. Two product meanings are worth restating because they are specific to this application:

  • clash-magenta marks an overlap between two chosen acts, and nothing else.
  • sunset-orange marks connectivity and synchronisation state.

Both must be paired with a label or icon, because colour alone is not a sufficient signal.

7.2 Typography

Plus Jakarta Sans throughout. Headings use weights 600 to 800, body and labels use 400 to 500. The font embed must include weights 400, 500, 600, 700, and 800. Numeric telemetry and compact code-like indicators may use a monospace fallback.

7.3 Implementation targets

  • src/styles/global.css
  • src/pages/index.astro
  • Shared components under src/components

8. Authentication

Public clients use OAuth 2.0 and OpenID Connect with PKCE against Microsoft Entra External ID. The PWA acquires tokens through MSAL and attaches a bearer token to every /api/* request. The Worker validates the token at the edge before any handler runs.

Authentication is dual-loop by design. The inner loop accepts a mock token so that local development needs no network or cloud tenant. The outer loop performs full JWKS validation with WebCrypto. Tenant partitioning and application registration naming are in Platform Architecture.