Skip to content

Architecture

T0mii edited this page Dec 30, 2025 · 3 revisions

Architecture & Engineering

Nebula adopts a Clean Architecture Lite approach to ensure scalability, testability, and separation of concerns.

1. Layers Structure

We enforce a strict unidirectional flow of dependency: presentation -> domain <- data.

  • domain/ (The Core)

    • Role: Defines what the app does (Business Logic).
    • Rules: Pure Dart code. NO Flutter, NO external implementation frameworks (Supabase, Firebase, Audio plugins).
    • Contents:
      • Entities: Pure models (e.g., Track).
      • Repositories (Interfaces): Contracts defining data operations (e.g., PlayerRepository).
  • data/ (The Implementation)

    • Role: Defines how things are done.
    • Rules: Implements Domain interfaces. Handles external libraries (APIs, Databases, Device Hardware).
    • Contents:
      • Repositories (Impl): Implementations of domain contracts (e.g., PlayerRepositoryImpl).
      • Data Sources: Direct API connectors or Service Wrappers (e.g., NebulaAudioHandler).
  • presentation/ (The UI)

    • Role: Displays state and captures user input.
    • Rules: Depends ONLY on Domain. Never talks directly to Data sources.
    • Contents:
      • Controllers/Blocs: State management (e.g., PlayerController).
      • Screens/Widgets: Flutter UI components.

2. The Player System

The music player is the heart of Nebula, designed to work flawlessly in the background.

  • Architecture Flow: UI (Buttons) -> PlayerController -> PlayerRepository -> NebulaAudioHandler -> AudioService -> JustAudio -> OS System APIs

  • Key Components:

    • NebulaAudioHandler (data):
      • Extends BaseAudioHandler from audio_service.
      • It is a Singleton service that runs in a separate isolate (conceptually) to keep the OS informed about playback.
      • Manages the notification (MediaStyle) and lock-screen controls.
      • Wraps just_audio for actual playback.
    • PlayerRepository (domain):
      • The clean interface that the App uses.
      • Abstracts away the complex AudioService streams into simple domain streams (currentTrack, isPlaying, position).
      • Queue Management: Handles logical queue operations like setQueue (ordered playback) and shuffleQueue (randomized).

3. Permissions (Android)

To enable background playback on modern Android (14+), we utilize:

  • FOREGROUND_SERVICE & FOREGROUND_SERVICE_MEDIA_PLAYBACK.
  • WAKE_LOCK to keep the CPU running while screen is off.
  • AudioServiceActivity in AndroidManifest.xml to correctly bind the Flutter Engine to the media service.

4. Data Persistence & Offline Strategy

Nebula is designed to work offline.

  • Hive: Used for storing local data like Playlists, Favorites, and Search History. It's fast, NoSQL, and minimal.
  • Dio: Handles file downloads with resilience (pausing/resuming support).
  • Offline Mode: The repository layer automatically switches between Remote sources (YouTube) and Local files based on availability.

5. Project Structure

We follow a feature-first organization inside lib/features/:

  • auth: Supabase authentication logic.
  • player: Playback logic and audio handler.
  • downloads: Management of offline content.
  • library: User's local music collection.
  • settings: App configuration.

Clone this wiki locally