-
Notifications
You must be signed in to change notification settings - Fork 2
Architecture
Aswin C edited this page Sep 9, 2026
·
2 revisions
boxlore is designed as a modular, multi-module Android project following clean architectural boundaries, unidirectional data flow (UDF), and strict dependency encapsulation.
-
Inward Dependency Direction
The application graph is structured so that high-level features depend inward on business logic, catalog repositories, and foundational infrastructure. Lower-level core libraries never depend on higher-level features or UI components. -
Feature Isolation
Features are decoupled horizontally. A feature module is strictly prohibited from declaring Gradle dependencies on, or importing code from, any other feature module. Cross-feature flows are coordinated via the app shell (:app) navigation graph. -
Single Composition Root
The entire runtime object graph is wired through a single, explicit dependency container:AppContainerin:app. No reflection-heavy runtime or compile-time DI frameworks (such as Hilt, Dagger, or Koin) are used. -
Architectural Guardrails as Code
Module isolation rules, dependency directions, and framework prohibitions are continuously validated in CI using Konsist tests in:core:testing.
The codebase is organized into three distinct tiers: Shell, Core Infrastructure, and Feature Domains.
boxlore/
├── app/ # Application shell & Composition Root
├── core/
│ ├── model/ # Shared domain models and enums
│ ├── network/ # Retrofit HTTP client, OkHttp, and network DTOs
│ ├── domain/ # Thin domain ports and decoupled interfaces
│ ├── database/ # Room database (boxlore_database), entities, and DAOs
│ ├── prefs/ # Jetpack DataStore and SharedPreferences abstractions
│ ├── analytics/ # Telemetry and analytics facade (no direct vendor leaks)
│ ├── catalog/ # Catalog orchestration, podcast/folder repositories, backup/restore
│ ├── rss/ # Custom XML parser, direct RSS feed repository, and OPML
│ ├── ranking/ # On-device LinUCB bandit scoring and adaptive Room database
│ ├── downloads/ # Episode download repository and background WorkManager workers
│ ├── playback/ # AndroidX Media3 playback service, queue, and audio session
│ ├── designsystem/ # Material 3 theme, solid surfaces, and reusable UI components
│ └── testing/ # Shared unit test fixtures, fakes, and Konsist architecture guards
└── feature/
├── home/ # Dashboard, editorial rails, and settings hub
├── player/ # Full player sheet scaffold, scrubber, chapters, transcripts
├── info/ # Podcast details, episode lists, and custom tag sheets
├── explore/ # Search, semantic concept discovery, and curiosity cards
├── library/ # Subscriptions, folders, downloads, history, and backup UI
├── onboarding/ # First-run setup, genre selection, and OPML import
├── briefing/ # Regional AI news briefing screen
└── widgets/ # Home-screen RemoteViews widgets (Now Playing, Library, Controls)
flowchart TB
app[":app (Shell)"]
subgraph features ["Feature Tier (Isolated)"]
home[":feature:home"]
player[":feature:player"]
info[":feature:info"]
explore[":feature:explore"]
library[":feature:library"]
onboarding[":feature:onboarding"]
briefing[":feature:briefing"]
widgets[":feature:widgets"]
end
subgraph domain_playback ["Service & Business Tier"]
playback[":core:playback"]
catalog[":core:catalog"]
downloads[":core:downloads"]
ranking[":core:ranking"]
end
subgraph infra ["Infrastructure & Data Tier"]
database[":core:database"]
network[":core:network"]
rss[":core:rss"]
prefs[":core:prefs"]
domain[":core:domain"]
analytics[":core:analytics"]
design[":core:designsystem"]
model[":core:model"]
end
app --> features
app --> domain_playback
app --> design
features --> domain_playback
features --> design
features --> model
features --> analytics
playback --> catalog
playback --> downloads
playback --> ranking
playback --> analytics
downloads --> catalog
catalog --> database
catalog --> network
catalog --> rss
catalog --> prefs
catalog --> domain
catalog --> ranking
catalog --> model
ranking --> database
ranking --> prefs
ranking --> model
rss --> database
rss --> domain
rss --> model
database --> model
network --> model
domain --> model
design --> model
- Zero Feature-to-Feature Dependencies: Features must never depend on or import other features.
-
Playback Layering:
:core:playbackdepends on:core:catalog.:core:catalogmust never depend on:core:playback. -
Design Isolation:
:core:catalogmust never depend on:core:designsystem. -
Clean Boundaries:
:core:catalogmust not expose:core:analyticsor:core:rankingviaapi()(strictlyimplementation). -
Vendor Decoupling: Feature modules never import PostHog directly. All analytics are routed through
:core:analytics. - Framework Restrictions: No Hilt, Koin, Dagger, or MockK are permitted anywhere in the repository.
-
Source File Size: Every Kotlin source file under
*/src/main/**must remain strictly under 1,000 lines.
Instead of delegating object graph creation to external annotation processors or reflection frameworks, boxlore initializes its runtime graph deterministically inside AppContainer:
┌────────────────────────────────────────┐
│ BoxLoreApplication │
│ └── appContainer = AppContainer(this) │
└───────────────────┬────────────────────┘
│ Reads container
┌──────────────┴──────────────┐
▼ ▼
MainActivity / NavHost WorkerFactory / Services
-
Instantiation Order:
Database$\to$ PodcastRepository$\to$ QueueRepository$\to$ PlaybackRepository$\to$ QueueManager$\to$ SmartDownloadManager. -
ViewModel Construction:
Complex ViewModels are constructed via assembler classes (e.g.,
HomeViewModelAssembler,SettingsViewModelAssembler,InfoViewModelAssembler). -
Test Isolation via Domain Ports:
The
:core:domainmodule defines thin ports (e.g.,LocalEpisodeCatalogPort,EpisodeSupplementPort). This enables fast, hermetic JVM unit tests utilizing shared fakes from:core:testingwithout instantiating database instances or mock engines.
Playback is orchestrated by BoxLorePlaybackService, a foreground AndroidX Media3 MediaLibrarySessionCallback service:
-
Single Playback Repository:
Exactly one UI-scopedPlaybackRepositoryinstance exists within the runtime graph. Background workers and separate navigation routes must never instantiate a second playback repository. -
Smart Queue Refill:
Ownership of queue replenishment is strictly held byBoxLorePlaybackService. When the active queue approaches exhaustion, the service automatically draws upcoming episodes from:- The active show (same-show continuation).
- Unfinished episodes from user history.
- Fresh episodes from subscribed podcasts.
- On-device recommendations.
-
Android Auto Resilience:
The media library session exposes structured browse roots (ROOT_RECENT,ROOT_SUBSCRIPTIONS,ROOT_QUEUE). Disconnection or vehicle shutdown events preserve the active session and mini-player state in permanent storage, enabling seamless playback resumption on reconnect. -
External Media Output:
Integrated Cast support allows streaming media to Google Cast receivers, automatically switching audio dispatch while synchronizing UI playback state.
To ensure uninterrupted app upgrades across major releases, the following contracts and historical bridges are permanently preserved:
| Area | Contract | Upgrade Bridge / Guard |
|---|---|---|
| Application ID | cx.aswin.boxlore |
Immutable shipping identifier |
| Package Namespaces | cx.aswin.boxlore.* |
Standardized root namespace |
| Main Room DB | boxlore_database |
Auto-migrated from legacy boxcast_database on startup |
| Ranking Room DB | adaptive_ranking_database |
Dedicated database owned strictly by :core:ranking
|
| DataStore | user_preferences |
Dedicated Protobuf/Preferences file |
| SharedPreferences | boxlore_* |
PrefsFileMigrator silently migrates legacy boxcast_* files |
| Legacy Workers | cx.aswin.boxcast.core.data.* |
LegacyWorkerFactory bridges historical FQCNs to :core:downloads
|
| Legacy Services | cx.aswin.boxcast.core.data.service.* |
Permanent stub classes preserved in AndroidManifest |
| Deep Link Schemes |
boxlore:// and boxcast://
|
Both schemes retained for backwards-compatible link handling |
| Episode Identifiers |
rss: prefix & negative IDs |
Stable hashing contract for custom RSS feeds and supplement items |
- Features Overview
- Subscription Folders
- Custom Tags & Icons
- Podcasting 2.0 & Transcripts
- Playback Engine
- Smart Queue & Downloads
- Backup & Restore
boxlore · Local-First Podcast Platform