System Design ( Day - 54 )

Manoj KumarManoj Kumar
3 min read

Design Music Player application

Functional Requirements
1. User can play, pause songs
2. User can create a playlist and add songs to playlist
2.1 Play entire playlist( Sequential manner, random manner etc… )
3. App should support multi output devices ( Bluetooth, speaker, wired speaker, headphones etc… )

Non Functional requirements.
1. Entire design should be easily scalable.
2. New Features( new output devices, new ways to play song from playlist ) can be easily

Building a fully-featured music app means juggling playlists, audio engines, output devices, and playback strategies. To keep our code clean, extensible, and maintainable, we can combine several classic patterns:

  1. Singleton – one global instance for managers

  2. Strategy – swap playback algorithms on-the-fly

  3. Adapter – unify disparate speaker APIs

  4. Factory – create device adapters by type

  5. Facade – expose a simple “play this song” API

Let’s explore each layer of our system.

1️⃣ Singletons: Centralized Managers

We want exactly one global:

  • MusicPlayerApplication

  • MusicPlayerFacade

  • PlaylistManager

  • PlaybackStrategyManager

  • DeviceManager

Each uses the Singleton pattern so every component gets the same instance when they call, for example, PlaylistManager.getInstance().


2️⃣ Strategy: Flexible Playback Order

Users expect sequential, shuffle, or custom playback. We define:

interface PlayStrategy {
  void setPlaylist(Playlist p);
  Song next();
  Song previous();
  void addToNext(Song s);
}

Concrete strategies like SequentialPlayStrategy, RandomPlayStrategy, and CustomPlayStrategy implement this interface. The PlaybackStrategyManager holds them and switches based on user choice.


3️⃣ Adapter: Supporting Multiple Speakers

Your audio engine must treat a Bluetooth speaker, a wired speaker, or headphones the same:

interface IAudioOutputDevice {
  void playAudio(Song song);
}

Each device has its own SDK:

  • BluetoothSpeakerAPI.playSoundViaBluetooth(data)

  • WiredSpeakerAPI.playSoundViaCable(data)

  • HeadphonesAPI.playSound(data)

We write adapters:


class BluetoothSpeakerAdapter implements IAudioOutputDevice {
  private BluetoothSpeakerAPI api;
  public void playAudio(Song song) {
    api.playSoundViaBluetooth(song.toBytes());
  }
}

And similarly for wired speakers and headphones.


4️⃣ Factory: Creating Adapters by Type

Instead of if–else everywhere, DeviceManager calls a DeviceFactory:

class AudioDeviceFactory {
  static IAudioOutputDevice getDevice(DeviceType type) {
    switch(type) {
      case BLUETOOTH: return new BluetoothSpeakerAdapter();
      case WIRED:     return new WiredSpeakerAdapter();
      case HEADPHONES:return new HeadphonesAdapter();
      // …
    }
  }
}

Now connecting a new device is as simple as changing the factory mapping.


5️⃣ Facade: One API to Rule Them All

At the top sits MusicPlayerFacade, our single entry point:

public class MusicPlayerFacade {
  private AudioEngine engine;  
  private PlaylistManager playlists;
  private PlayStrategyManager strategies;
  private DeviceManager devices;

  public void connectDevice(DeviceType type) { … }
  public void createPlaylist(String name) { … }
  public void addSong(String list, Song s) { … }
  public void setStrategy(StrategyType t) { … }
  public void play() {  
    Song s = strategies.getCurrent().next();
    engine.play(devices.getCurrent(), s);
  }
  public void pause() { engine.pause(); }
  public void next()  { /* … */ }
  public void previous() { /* … */ }
}

Clients call only these facade methods and never worry about underlying adapters, factories, or strategy switches.


🚀 Why This Architecture Rocks

  • Single Responsibility: Each class does one thing

  • Open/Closed: Add new speaker types, strategies, or playlist rules without touching existing code

  • Low Coupling: Facade shields clients from inner complexity

  • High Cohesion: Related functionality lives together

0
Subscribe to my newsletter

Read articles from Manoj Kumar directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Manoj Kumar
Manoj Kumar