Skip to content
Rymflux Documentation
Esc
navigateopen⌘Jpreview
On this page

ADR-005: Inter-component communication — Tauri events

ADR-005: Inter-component communication — Tauri events

Status

Accepted

Context

The primer and charter identify an event bus as a v2 concern (scope.md: “Event system / event bus” is out of scope for v1). However, several requirements already imply event-driven communication:

  • FR-CORE-1: “the engine emits a ‘playing’ event”
  • The queue must advance when a track ends (knowing this requires a notification from the playback engine)
  • The library must record playback history
  • The search manager must push results to the frontend progressively (ADR-007)

Components in v1 are: Rust backend (plugin runtime, playback engine, library, search manager) and Svelte frontend (rooms, shell, UI).

Options:

  • Tauri event system (app_handle.emit_all / listen): Built-in, bidirectional, no extra dependencies. Events are serialized as JSON. Frontend subscribes with listen().
  • Tauri commands (invoke): Request-response only. The frontend initiates. Cannot push data from backend to frontend without polling.
  • Custom event bus (channels/actors in Rust): A Rust-side actor system (e.g., tokio::sync::broadcast or actix). Handles internal Rust↔Rust events. Backend→frontend still needs Tauri events.
  • Full external event bus (Redis pub/sub, NATS): Overkill for a local-first desktop app.

Decision

Use Tauri’s built-in event system for all cross-boundary (Rust ↔ Svelte) communication in v1. Use tokio::sync::broadcast channels for Rust↔Rust communication within the backend.

Architecture:

  Plugin Runtime ──tokio::broadcast──→ Search Manager
       ↓                                    ↓
  Tauri emit("search_result", payload)    Tauri emit("search_done")
       ↓                                    ↓
  Svelte listen("search_result")         Svelte listen("search_done")
       ↓                                    ↓
  Room renders results progressively     Room shows "search complete"
  Playback Engine ──tokio::broadcast──→ Library (record history)
       ↓                                    ↓
  Tauri emit("playback_state")          Tauri emit("history_updated")
       ↓                                    ↓
  Mini-player / Shell UI                 Library UI

Key rules:

  • Rust↔Rust events use tokio::sync::broadcast (or watch for state that must always have a value).
  • Backend→Frontend events use app_handle.emit_all.
  • Frontend→Backend events use app_handle.listen (rare — most frontend→backend is invoke).
  • The S3 event bus backlog item (W5) is re-scoped to “replace Tauri events with a formal event bus” when v2 requires cross-process or cross-instance events.

Alternatives considered

  • Frontend-polled invoke (frontend calls get_current_track every 100ms): Works but wastes CPU, increases latency, and doesn’t compose with progressive search results. Events are the right model for push-based communication.
  • Rust actor system (actix): Adds a framework dependency for what is fundamentally point-to-point channel communication. tokio::sync::broadcast and watch cover the v1 patterns without a framework.
  • WebSocket server from Rust: Would decouple frontend from Tauri’s event system. Adds a local WebSocket server, connection management, reconnection logic. None of this is needed when both processes are in the same Tauri webview.

Consequences

Positive:

  • Zero additional dependencies for inter-component communication.
  • Tauri events are serialized as JSON and delivered asynchronously — fits the FR-CORE-1 “playing event” requirement naturally.
  • tokio::sync::broadcast gives clean fan-out (one producer, N consumers) for events like search results that multiple components may observe.
  • The v2 event bus migration has a clear path: keep the event contract, replace the transport.

Negative:

  • Tauri events are not persisted. If the frontend isn’t listening (e.g., webview not yet loaded), the event is lost. Mitigation: stateful events use watch channels (always have the latest value).
  • Events are serialized as JSON on every emit. For high-frequency events (audio position updates at 10Hz), this adds serialization overhead. Mitigation: throttle position updates to 1Hz, or use a watch channel that the frontend polls lazily.
  • Tauri’s event system is single-process. Cross-process events (e.g., a future mobile companion) need the v2 event bus.

References

  • Scope.md: “Event system / event bus” is v2 (out of scope)
  • FR-CORE-1, FR-CORE-4 (functional.md)
  • ADR-007 (search manager architecture)

Was this page helpful?