The Question
FE DesignMusic Streaming Service Shell Architecture
Design the frontend architecture for a music streaming platform similar to Spotify. Your solution must address the challenge of maintaining persistent audio playback during page transitions, managing large-scale track lists with high performance, and implementing a robust global state for playback control across various UI components (Sidebar, Player Bar, and Main Views). Discuss your strategies for rendering optimization, state synchronization, and handling real-time audio events within a standard web framework.
React
TanStack Query
Zustand
TypeScript
HTML5 Media API
CSS Modules
SPA
Questions & Insights
Clarifying Questions
Q1: Is persistent playback required during navigation?
Assumption: Yes, the audio must continue playing seamlessly as the user navigates between different views (Home, Search, Library).
Q2: Should the MVP support offline playback?
Assumption: No, the MVP will focus on online streaming to reduce complexity regarding Service Workers and local encrypted storage.
Q3: What is the target device priority?
Assumption: Desktop-first web experience, as it allows for a more complex "persistent shell" layout, with responsive adjustments for mobile web.
Q4: How many tracks/items might be in a single playlist?
Assumption: Playlists can exceed 10,000 items; therefore, list virtualization is a critical requirement for memory management.
Crash Strategy
The Core Bottleneck: Preventing audio "stutter" or restarts during navigation.
Strategy: Use a Single Page Application (SPA) with a persistent App Shell architecture. The Audio Engine lives at the root level, decoupled from the page-level components.
Progressive Flow:
Audio Layer: Implement a singleton Audio Controller wrapping the HTML5 Media API.
Global State: Sync current track, progress, and playback status across the UI.
Layout Layer: Implement a persistent Sidebar and Player bar with a dynamic Main Content area.
Data Optimization: Use virtualization for track lists and optimistic UI for social features (liking/saving).
Elite Bonus Points
Media Session API: Integration with OS-level media controls (hardware play/pause buttons, lock screen metadata).
Audio Buffering Strategy: Implementing a pre-fetch mechanism for the "next" song in the queue to ensure zero-latency transitions.
Optimistic UI: Instant feedback when a user "Likes" a track or adds it to a playlist, resolving the server response in the background.
Design Breakdown
Requirements
Functional Requirements:
Search tracks, artists, and albums.
Continuous audio playback with Play/Pause/Skip/Seek.
View and manage playlists (Create/Add/Remove).
Browse curated categories.
Non-Functional Requirements:
Seamlessness: No audio interruption during routing.
Performance: Initial load < 2s, track start latency < 500ms.
Scalability: Handle lists of 10k+ tracks via virtualization.
Accessibility: Full keyboard navigation and ARIA labels for player controls.
Design Summary
Concise Summary: A persistent App Shell SPA that isolates the Audio Engine from the view-switching logic to ensure uninterrupted playback.
Major Components:
Audio Engine: A low-level singleton service managing the
<audio> element and playback buffer.Playback Controller: Application-layer logic that bridges UI interactions to the Audio Engine.
Virtual List: A high-performance component for rendering massive track lists efficiently.
Navigation Manager: Handles routing within the main content area without refreshing the App Shell.
CUJ Walkthrough:
Play a Song: User clicks a track row; the
Playback Controller updates the Global Playback State; the Audio Engine catches the change, loads the stream, and begins playback.Navigate while Playing: User clicks "Search"; the
Router replaces the Page component in the Main Content area; the Player Bar (child of App Shell) remains mounted and unaffected.Simplicity Audit: This architecture uses standard HTML5 Audio and SPA routing, avoiding the complexity of Micro-frontends or Web Audio API nodes until advanced DSP (Digital Signal Processing) is required.
Architecture Decision Rationale:
Why this?: An SPA is mandatory for persistent audio. Decoupling the engine from the UI allows for easier testing and prevents UI re-renders from affecting playback timing.
Requirement Satisfaction: Meets the "no-gap" requirement via a persistent shell and "performance" requirement via virtualization.
System Diagram
Architecture Deep Dive
Presentation Layer
Component Hierarchy: The
App Shell wraps the entire application. The Layout defines fixed areas (Sidebar, Player Bar) and a dynamic Main Content area where Page components (Home, Search, PlaylistView) are swapped by the router.Interaction Layer:
Event Handling: Global keyboard listeners for Space (Play/Pause) and Arrows (Seek).
Accessibility: Use
role="slider" for volume and progress bars; ensure focus management when navigating between pages.Responsive Design: On mobile, the Sidebar converts to a Bottom Navigation bar, and the Player Bar expands to a full-screen modal.
Rendering Layer:
Reconciliation: Use
React.memo on TrackRow components to prevent re-rendering the whole list when the playback progress updates.Virtualization: Use
react-window or virtuoso for the TrackList to ensure only the visible ~10 rows are in the DOM.Application Layer
Data Fetching Layer: Use
TanStack Query for caching track metadata. Implement "Infinite Loading" for search results.State Management Layer:
Global State: Managed via Zustand or Redux to track
currentTrack, isPlaying, and playQueue. Sync: The
Player Bar subscribes to playbackTime updates (emitted every 200ms) to update the progress slider.Routing & Navigation: Nested routing where the parent route is the Shell. Use URL-based state for search queries (e.g.,
/search?q=artist) to allow bookmarking.Domain Layer
Business Rules:
Queue Logic: Logic for Shuffle (Fisher-Yates) and Repeat (Off/One/All) resides here.
Validation: Prevent playing premium-only tracks if the user entity has a
free status.Entities: Clean TypeScript interfaces for
Track, Album, and Artist to decouple the UI from backend DTO (Data Transfer Object) changes.Infrastructure Layer
Audio Engine: A Singleton class that controls a single
new Audio() instance. It exposes methods like play(trackUrl), pause(), and seek(time). It emits events that the Application Layer listens to.Storage: Use
localStorage to persist the user's volume preference and the last played track/queue so the session can be restored on refresh.Wrap Up
Wrap-up
Evaluation: This design prioritizes the "Seamless Playback" and "Performance" requirements of a music app.
Trade-offs: By using a single
<audio> element, we simplify state management but lose the ability to perform complex crossfading between tracks (which would require two elements and the Web Audio API).Optimization: To improve track start times, the
Feature Container can trigger a link rel="prefetch" for the audio stream of the next track in the queue once the current track reaches 90% completion.