# Modular Architecture Design **Referenced Files in This Document** - [settings.gradle.kts](file://settings.gradle.kts) - [build.gradle.kts](file://build.gradle.kts) - [gradle/libs.versions.toml](file://gradle/libs.versions.toml) - [app/build.gradle.kts](file://app/build.gradle.kts) - [core/model/build.gradle.kts](file://core/model/build.gradle.kts) - [core/data/build.gradle.kts](file://core/data/build.gradle.kts) - [core/domain/build.gradle.kts](file://core/domain/build.gradle.kts) - [core/ui/build.gradle.kts](file://core/ui/build.gradle.kts) - [core/domain/src/main/java/com/suvojeet/suvmusic/core/domain/repository/LibraryRepository.kt](file://core/domain/src/main/java/com/suvojeet/suvmusic/core/domain/repository/LibraryRepository.kt) - [media-source/src/main/java/com/suvojeet/suvmusic/providers/lyrics/LyricsProvider.kt](file://media-source/src/main/java/com/suvojeet/suvmusic/providers/lyrics/LyricsProvider.kt) - [scrobbler/src/main/java/com/suvojeet/suvmusic/lastfm/LastFmRepository.kt](file://scrobbler/src/main/java/com/suvojeet/suvmusic/lastfm/LastFmRepository.kt) - [updater/src/main/kotlin/com/suvojeet/suvmusic/updater/UpdaterModule.kt](file://updater/src/main/kotlin/com/suvojeet/suvmusic/updater/UpdaterModule.kt) - [lyric-simpmusic/src/main/java/com/suvojeet/suvmusic/simpmusic/SimpMusicLyricsProvider.kt](file://lyric-simpmusic/src/main/java/com/suvojeet/suvmusic/simpmusic/SimpMusicLyricsProvider.kt) - [lyric-lrclib/src/main/java/com/suvojeet/suvmusic/lrclib/LrcLibLyricsProvider.kt](file://lyric-lrclib/src/main/java/com/suvojeet/suvmusic/lrclib/LrcLibLyricsProvider.kt) - [lyric-kugou/src/main/java/com/suvojeet/suvmusic/kugou/KuGouLyricsProvider.kt](file://lyric-kugou/src/main/java/com/suvojeet/suvmusic/kugou/KuGouLyricsProvider.kt) - [extractor/src/main/java/com/suvojeet/suvmusic/newpipe/NewPipeDownloaderImpl.kt](file://extractor/src/main/java/com/suvojeet/suvmusic/newpipe/NewPipeDownloaderImpl.kt) ## Table of Contents 1. [Introduction](#introduction) 2. [Project Structure](#project-structure) 3. [Core Components](#core-components) 4. [Architecture Overview](#architecture-overview) 5. [Detailed Component Analysis](#detailed-component-analysis) 6. [Dependency Analysis](#dependency-analysis) 7. [Performance Considerations](#performance-considerations) 8. [Troubleshooting Guide](#troubleshooting-guide) 9. [Conclusion](#conclusion) 10. [Appendices](#appendices) ## Introduction This document explains SuvMusic’s modular architecture designed around a multi-module Gradle structure. The app is organized into: - Core modules: model, data, domain, ui - Feature modules: media-source (lyrics providers), scrobbler (Last.fm), updater, extractor (NewPipe), and lyric-* modules for specific lyrics providers The design emphasizes separation of concerns, testability, and independent development. Modules communicate via well-defined interfaces and dependency injection, enabling teams to work on features in isolation while maintaining a cohesive product. ## Project Structure The project uses a hierarchical Gradle setup with a root build script and per-module build scripts. Module inclusion is centralized in settings, and shared dependency versions are managed in libs.versions.toml. ```mermaid graph TB Root["Root Build Script
build.gradle.kts"] Settings["Settings Script
settings.gradle.kts"] Versions["Versions Catalog
gradle/libs.versions.toml"] App["App Module
app/build.gradle.kts"] CoreModel["Core Model
core:model/build.gradle.kts"] CoreData["Core Data
core:data/build.gradle.kts"] CoreDomain["Core Domain
core:domain/build.gradle.kts"] CoreUI["Core UI
core:ui/build.gradle.kts"] MediaSource["Media Source
media-source/build.gradle.kts"] Scrobbler["Scrobbler
scrobbler/build.gradle.kts"] Updater["Updater
updater/build.gradle.kts"] Extractor["Extractor
extractor/build.gradle.kts"] LyricSimp["Lyric Provider: SimpMusic
lyric-simpmusic/build.gradle.kts"] LyricLrcLib["Lyric Provider: LRCLIB
lyric-lrclib/build.gradle.kts"] LyricKugou["Lyric Provider: Kugou
lyric-kugou/build.gradle.kts"] Settings --> App Settings --> CoreModel Settings --> CoreData Settings --> CoreDomain Settings --> CoreUI Settings --> MediaSource Settings --> Scrobbler Settings --> Updater Settings --> Extractor Settings --> LyricSimp Settings --> LyricLrcLib Settings --> LyricKugou Root --> App Root --> CoreModel Root --> CoreData Root --> CoreDomain Root --> CoreUI Root --> MediaSource Root --> Scrobbler Root --> Updater Root --> Extractor Root --> LyricSimp Root --> LyricLrcLib Root --> LyricKugou Versions --> App Versions --> CoreModel Versions --> CoreData Versions --> CoreDomain Versions --> CoreUI Versions --> MediaSource Versions --> Scrobbler Versions --> Updater Versions --> Extractor Versions --> LyricSimp Versions --> LyricLrcLib Versions --> LyricKugou ``` **Diagram sources** - [settings.gradle.kts:18-30](file://settings.gradle.kts#L18-L30) - [build.gradle.kts:1-10](file://build.gradle.kts#L1-L10) - [gradle/libs.versions.toml:1-162](file://gradle/libs.versions.toml#L1-L162) - [app/build.gradle.kts:254-265](file://app/build.gradle.kts#L254-L265) **Section sources** - [settings.gradle.kts:18-30](file://settings.gradle.kts#L18-L30) - [build.gradle.kts:1-10](file://build.gradle.kts#L1-L10) - [gradle/libs.versions.toml:1-162](file://gradle/libs.versions.toml#L1-L162) ## Core Components The core modules form the foundation of the app: - Core Model: Defines immutable data types used across the app (e.g., Song, Album, Playlist). - Core Domain: Exposes business interfaces (e.g., Repository contracts) consumed by higher layers. - Core Data: Implements repositories and persistence using Room, with DI support. - Core UI: Provides reusable UI components and Compose infrastructure. These modules are intentionally thin and free of platform-specific UI or networking code, ensuring reuse and testability. **Section sources** - [core/model/build.gradle.kts:1-40](file://core/model/build.gradle.kts#L1-L40) - [core/domain/build.gradle.kts:1-30](file://core/domain/build.gradle.kts#L1-L30) - [core/data/build.gradle.kts:1-44](file://core/data/build.gradle.kts#L1-L44) - [core/ui/build.gradle.kts:1-43](file://core/ui/build.gradle.kts#L1-L43) ## Architecture Overview SuvMusic follows a layered architecture: - Presentation layer (app) consumes UI components from core:ui and orchestrates features. - Domain layer defines contracts (repositories) used by presentation and features. - Data layer implements repositories and persists data using Room. - Feature modules encapsulate cross-cutting concerns (e.g., lyrics providers, scrobbling, updates, extraction). ```mermaid graph TB subgraph "Presentation Layer" App["App (app)"] CoreUI["Core UI (core:ui)"] end subgraph "Domain Layer" CoreDomain["Core Domain (core:domain)"] LibraryRepo["LibraryRepository (interface)"] end subgraph "Data Layer" CoreData["Core Data (core:data)"] Room["Room (entities, DAOs, database)"] end subgraph "Feature Modules" MediaSource["Media Source (media-source)"] Scrobbler["Scrobbler (scrobbler)"] Updater["Updater (updater)"] Extractor["Extractor (extractor)"] LyricSimp["Lyric Provider: SimpMusic"] LyricLrcLib["Lyric Provider: LRCLIB"] LyricKugou["Lyric Provider: Kugou"] end App --> CoreUI App --> CoreDomain App --> MediaSource App --> Scrobbler App --> Updater App --> Extractor App --> LyricSimp App --> LyricLrcLib App --> LyricKugou CoreDomain --> LibraryRepo CoreData --> Room CoreData --> LibraryRepo MediaSource --> LibraryRepo Scrobbler --> LibraryRepo Updater --> LibraryRepo Extractor --> LibraryRepo LyricSimp --> MediaSource LyricLrcLib --> MediaSource LyricKugou --> MediaSource ``` **Diagram sources** - [app/build.gradle.kts:254-265](file://app/build.gradle.kts#L254-L265) - [core/domain/src/main/java/com/suvojeet/suvmusic/core/domain/repository/LibraryRepository.kt:11-36](file://core/domain/src/main/java/com/suvojeet/suvmusic/core/domain/repository/LibraryRepository.kt#L11-L36) - [media-source/src/main/java/com/suvojeet/suvmusic/providers/lyrics/LyricsProvider.kt:7-49](file://media-source/src/main/java/com/suvojeet/suvmusic/providers/lyrics/LyricsProvider.kt#L7-L49) - [scrobbler/src/main/java/com/suvojeet/suvmusic/lastfm/LastFmRepository.kt:8-42](file://scrobbler/src/main/java/com/suvojeet/suvmusic/lastfm/LastFmRepository.kt#L8-L42) - [updater/src/main/kotlin/com/suvojeet/suvmusic/updater/UpdaterModule.kt:10-19](file://updater/src/main/kotlin/com/suvojeet/suvmusic/updater/UpdaterModule.kt#L10-L19) - [extractor/src/main/java/com/suvojeet/suvmusic/newpipe/NewPipeDownloaderImpl.kt:16-19](file://extractor/src/main/java/com/suvojeet/suvmusic/newpipe/NewPipeDownloaderImpl.kt#L16-L19) ## Detailed Component Analysis ### Core Domain: Repository Contracts The domain layer defines contracts that encapsulate business logic. The LibraryRepository interface demonstrates a typical contract for library operations (playlists, albums, artists), exposing both suspend functions and reactive Flow streams. ```mermaid classDiagram class LibraryRepository { +savePlaylist(playlist) +savePlaylistSongs(playlistId, songs) +appendPlaylistSongs(playlistId, songs, startOrder) +getCachedPlaylistSongs(playlistId) Song[] +getCachedPlaylistSongsFlow(playlistId) Flow~Song[]~ +getPlaylistSongCountFlow(playlistId) Flow~Int~ +isSongInPlaylist(playlistId, songId) Boolean +updatePlaylistThumbnail(playlistId, thumbnailUrl) +updatePlaylistName(playlistId, name) +replacePlaylistSongs(playlistId, songs) +removePlaylist(playlistId) +removeSongFromPlaylist(playlistId, songId) +addSongToPlaylist(playlistId, song) +saveAlbum(album) +removeAlbum(albumId) +isPlaylistSaved(playlistId) Flow~Boolean~ +isAlbumSaved(albumId) Flow~Boolean~ +getPlaylistById(id) LibraryItem? +getSavedPlaylists() Flow~PlaylistDisplayItem[]~ +getSavedAlbums() Flow~LibraryItem[]~ +saveArtist(artist) +removeArtist(artistId) +getSavedArtists() Flow~LibraryItem[]~ +isArtistSaved(artistId) Flow~Boolean~ } ``` **Diagram sources** - [core/domain/src/main/java/com/suvojeet/suvmusic/core/domain/repository/LibraryRepository.kt:11-36](file://core/domain/src/main/java/com/suvojeet/suvmusic/core/domain/repository/LibraryRepository.kt#L11-L36) **Section sources** - [core/domain/src/main/java/com/suvojeet/suvmusic/core/domain/repository/LibraryRepository.kt:11-36](file://core/domain/src/main/java/com/suvojeet/suvmusic/core/domain/repository/LibraryRepository.kt#L11-L36) ### Core Data: Repository Implementation and Persistence The data layer implements domain contracts and manages persistence. It depends on core:model and core:domain, integrates Room for database operations, and uses Hilt for DI. ```mermaid sequenceDiagram participant Domain as "Domain Layer" participant Data as "Core Data" participant Room as "Room Database" Domain->>Data : "savePlaylist(playlist)" Data->>Room : "Insert/Update playlist entities" Room-->>Data : "Success/Failure" Data-->>Domain : "Unit or Result" ``` **Diagram sources** - [core/data/build.gradle.kts:32-43](file://core/data/build.gradle.kts#L32-L43) **Section sources** - [core/data/build.gradle.kts:32-43](file://core/data/build.gradle.kts#L32-L43) ### Core UI: Compose Infrastructure The UI module depends on core:model and integrates Compose, Material3, and Hilt. It provides reusable UI building blocks and Compose-specific infrastructure. ```mermaid flowchart TD Start(["Initialize UI"]) --> Compose["Compose UI Setup"] Compose --> Material["Material3 Theming"] Material --> Hilt["Hilt DI Integration"] Hilt --> Components["Reusable UI Components"] Components --> End(["Render Screens"]) ``` **Diagram sources** - [core/ui/build.gradle.kts:31-42](file://core/ui/build.gradle.kts#L31-L42) **Section sources** - [core/ui/build.gradle.kts:31-42](file://core/ui/build.gradle.kts#L31-L42) ### Feature Module: Lyrics Providers (media-source and lyric-*) The media-source module defines a generic LyricsProvider interface. Specific providers (LRCLIB, SimpMusic, Kugou) implement this interface, enabling pluggable lyrics fetching. ```mermaid classDiagram class LyricsProvider { +name : String +getLyrics(id, title, artist, duration, album) Result~String~ +getAllLyrics(id, title, artist, duration, album, callback) } class LrcLibLyricsProvider { +name : "LRCLIB" +getLyrics(...) +getAllLyrics(...) } class SimpMusicLyricsProvider { +name : "SimpMusic" +getLyrics(...) +getAllLyrics(...) } class KuGouLyricsProvider { +name : "Kugou" +getLyrics(...) +getAllLyrics(...) } LyricsProvider <|.. LrcLibLyricsProvider LyricsProvider <|.. SimpMusicLyricsProvider LyricsProvider <|.. KuGouLyricsProvider ``` **Diagram sources** - [media-source/src/main/java/com/suvojeet/suvmusic/providers/lyrics/LyricsProvider.kt:7-49](file://media-source/src/main/java/com/suvojeet/suvmusic/providers/lyrics/LyricsProvider.kt#L7-L49) - [lyric-lrclib/src/main/java/com/suvojeet/suvmusic/lrclib/LrcLibLyricsProvider.kt:13-15](file://lyric-lrclib/src/main/java/com/suvojeet/suvmusic/lrclib/LrcLibLyricsProvider.kt#L13-L15) - [lyric-simpmusic/src/main/java/com/suvojeet/suvmusic/simpmusic/SimpMusicLyricsProvider.kt:10-20](file://lyric-simpmusic/src/main/java/com/suvojeet/suvmusic/simpmusic/SimpMusicLyricsProvider.kt#L10-L20) - [lyric-kugou/src/main/java/com/suvojeet/suvmusic/kugou/KuGouLyricsProvider.kt:10-22](file://lyric-kugou/src/main/java/com/suvojeet/suvmusic/kugou/KuGouLyricsProvider.kt#L10-L22) **Section sources** - [media-source/src/main/java/com/suvojeet/suvmusic/providers/lyrics/LyricsProvider.kt:7-49](file://media-source/src/main/java/com/suvojeet/suvmusic/providers/lyrics/LyricsProvider.kt#L7-L49) - [lyric-lrclib/src/main/java/com/suvojeet/suvmusic/lrclib/LrcLibLyricsProvider.kt:13-15](file://lyric-lrclib/src/main/java/com/suvojeet/suvmusic/lrclib/LrcLibLyricsProvider.kt#L13-L15) - [lyric-simpmusic/src/main/java/com/suvojeet/suvmusic/simpmusic/SimpMusicLyricsProvider.kt:10-20](file://lyric-simpmusic/src/main/java/com/suvojeet/suvmusic/simpmusic/SimpMusicLyricsProvider.kt#L10-L20) - [lyric-kugou/src/main/java/com/suvojeet/suvmusic/kugou/KuGouLyricsProvider.kt:10-22](file://lyric-kugou/src/main/java/com/suvojeet/suvmusic/kugou/KuGouLyricsProvider.kt#L10-L22) ### Feature Module: Scrobbler (Last.fm) The scrobbler module encapsulates Last.fm integration. It exposes a repository that delegates to a network client, handles authentication, and provides scrobbling operations. ```mermaid sequenceDiagram participant App as "App" participant Repo as "LastFmRepository" participant Client as "LastFmClient" App->>Repo : "fetchSession(token)" Repo->>Client : "fetchSession(token)" Client-->>Repo : "Authentication" Repo-->>App : "Result" ``` **Diagram sources** - [scrobbler/src/main/java/com/suvojeet/suvmusic/lastfm/LastFmRepository.kt:8-42](file://scrobbler/src/main/java/com/suvojeet/suvmusic/lastfm/LastFmRepository.kt#L8-L42) **Section sources** - [scrobbler/src/main/java/com/suvojeet/suvmusic/lastfm/LastFmRepository.kt:8-42](file://scrobbler/src/main/java/com/suvojeet/suvmusic/lastfm/LastFmRepository.kt#L8-L42) ### Feature Module: Updater The updater module provides update checking capabilities and integrates with Hilt for dependency injection. ```mermaid classDiagram class UpdaterModule { +provideUpdateChecker(okHttpClient) UpdateChecker } ``` **Diagram sources** - [updater/src/main/kotlin/com/suvojeet/suvmusic/updater/UpdaterModule.kt:10-19](file://updater/src/main/kotlin/com/suvojeet/suvmusic/updater/UpdaterModule.kt#L10-L19) **Section sources** - [updater/src/main/kotlin/com/suvojeet/suvmusic/updater/UpdaterModule.kt:10-19](file://updater/src/main/kotlin/com/suvojeet/suvmusic/updater/UpdaterModule.kt#L10-L19) ### Feature Module: Extractor (NewPipe) The extractor module adapts OkHttp to NewPipe’s Downloader interface, enabling authenticated requests and robust header handling. ```mermaid classDiagram class NewPipeDownloaderImpl { -client : OkHttpClient -cookieProvider : () -> String +execute(extractorRequest) Response } ``` **Diagram sources** - [extractor/src/main/java/com/suvojeet/suvmusic/newpipe/NewPipeDownloaderImpl.kt:16-19](file://extractor/src/main/java/com/suvojeet/suvmusic/newpipe/NewPipeDownloaderImpl.kt#L16-L19) **Section sources** - [extractor/src/main/java/com/suvojeet/suvmusic/newpipe/NewPipeDownloaderImpl.kt:16-19](file://extractor/src/main/java/com/suvojeet/suvmusic/newpipe/NewPipeDownloaderImpl.kt#L16-L19) ## Dependency Analysis Module dependencies are declared centrally in the app module and enforced by Gradle. The app depends on core modules and feature modules, while core modules depend on each other in a unidirectional manner. ```mermaid graph LR App["app"] --> CoreModel["core:model"] App --> CoreData["core:data"] App --> CoreDomain["core:domain"] App --> CoreUI["core:ui"] App --> MediaSource["media-source"] App --> Scrobbler["scrobbler"] App --> Updater["updater"] App --> Extractor["extractor"] App --> LyricSimp["lyric-simpmusic"] App --> LyricLrcLib["lyric-lrclib"] App --> LyricKugou["lyric-kugou"] CoreData --> CoreModel CoreData --> CoreDomain CoreUI --> CoreModel ``` **Diagram sources** - [app/build.gradle.kts:254-265](file://app/build.gradle.kts#L254-L265) - [core/data/build.gradle.kts:32-33](file://core/data/build.gradle.kts#L32-L33) - [core/ui/build.gradle.kts:32](file://core/ui/build.gradle.kts#L32) **Section sources** - [app/build.gradle.kts:254-265](file://app/build.gradle.kts#L254-L265) - [core/data/build.gradle.kts:32-33](file://core/data/build.gradle.kts#L32-L33) - [core/ui/build.gradle.kts:32](file://core/ui/build.gradle.kts#L32) ## Performance Considerations - Build speed: Modularization allows parallel builds and incremental compilation. Team members can focus on isolated modules, reducing merge conflicts and enabling faster CI cycles. - Testability: Interfaces in core:domain and feature modules enable mocking and unit testing without launching the full app. - Resource optimization: The app restricts supported ABIs and locales to reduce APK size and improve cold start performance. - Network efficiency: Feature modules (e.g., lyric providers) encapsulate network logic, minimizing cross-feature coupling and enabling targeted caching strategies. ## Troubleshooting Guide - Dependency resolution failures: Verify module inclusion in settings and ensure libs.versions.toml entries align with module build scripts. - DI binding errors: Confirm Hilt modules are installed in the appropriate component and that qualifiers are consistent across modules. - Feature module not compiling: Check that feature modules declare dependencies on core:model and core:domain when needed. - Network-related issues: Review extractor’s Downloader implementation and OkHttp configurations used by feature modules. **Section sources** - [settings.gradle.kts:18-30](file://settings.gradle.kts#L18-L30) - [gradle/libs.versions.toml:1-162](file://gradle/libs.versions.toml#L1-162) - [extractor/src/main/java/com/suvojeet/suvmusic/newpipe/NewPipeDownloaderImpl.kt:16-19](file://extractor/src/main/java/com/suvojeet/suvmusic/newpipe/NewPipeDownloaderImpl.kt#L16-L19) ## Conclusion SuvMusic’s modular architecture cleanly separates concerns across core and feature modules. Well-defined interfaces and dependency injection enable independent development, testing, and deployment. The design scales with new features—each can be introduced as a separate module, integrated via DI, and validated independently, supporting long-term maintainability and team productivity. ## Appendices - Module versioning and dependency management are handled centrally via libs.versions.toml, ensuring consistent versions across modules and simplifying updates. - Adding a new feature: Create a new module, define its public interfaces in core or a dedicated module, implement functionality, and wire it into the app via DI and module dependencies.