Module Structure¶
The project is organized into three module categories: app, feature, and shared.
Module Tree¶
CyclingAssistant/
├── app/ # Shell — navigation, theme, Hilt bootstrap, Room DB
├── build-logic/ # Convention plugins (cycling.feature, cycling.library, etc.)
├── feature/
│ ├── bridge/ # Cross-feature communication (alphabetical pair names)
│ │ ├── connection-session/ # connections ↔ session
│ │ ├── destination-nutrition/ # destinations ↔ nutrition
│ │ ├── destination-poi/ # destinations ↔ poi
│ │ ├── destination-session/ # destinations ↔ session
│ │ ├── nutrition-session/ # nutrition ↔ session
│ │ ├── nutrition-settings/ # nutrition ↔ settings
│ │ ├── poi-settings/ # poi ↔ settings
│ │ ├── profile-session/ # profile ↔ session
│ │ └── session-settings/ # session ↔ settings
│ │ ├── api/ # Interfaces exposed to consumers
│ │ └── impl/ # Implementations wiring to provider internals
│ ├── connections/ # BLE device connection and management
│ ├── dashboard/ # Main dashboard with expandable menu
│ ├── destinations/ # Destination selection feature
│ ├── locale/ # App language persistence and observation
│ ├── nutrition/ # Nutrition tracking and reminders
│ ├── poi/ # POI type selection and active session POI actions
│ ├── profile/ # Rider profile management
│ ├── sensor/
│ │ └── power/ # Power meter test mode and observation
│ ├── session/ # Session tracking, stats display, GPX export
│ ├── settings/ # App settings (theme, language, stats display)
│ └── theme/ # App theme persistence and observation
└── shared/
├── altitude/ # Altitude gain calculator
├── ble/ # BLE primitives (GATT, scanning, permissions)
├── concurrent/ # Coroutine dispatchers, suspendRunCatching, ConcurrentFactory
├── design-system/ # UI theme, colors, spacing, components
├── di/ # Hilt qualifier annotations
├── distance/ # Distance calculator
├── error/ # Error mapping utilities
├── graphics/ # Bitmap utilities
├── id/ # ID generator
├── location/ # Location services (validator, smoother, data sources)
├── map/ # Google Maps route rendering constants & utilities
├── sensor-protocol/ # BLE sensor data parsing (cycling power)
└── testing/ # Test utilities
app¶
The shell module — owns the AppDatabase (Room), AppNavHost navigation wiring, theme setup, and Hilt bootstrap (@HiltAndroidApp). Every feature and shared module is included here.
feature¶
Each feature module contains di/, domain/, data/, and presentation/ packages. Features are fully isolated and communicate only through bridge modules.
Bridge modules live under feature/bridge/ and are named as alphabetically-ordered pairs of the two features they connect (e.g., destination-session, not session-destination). Each bridge has an api/ submodule (interfaces) and an impl/ submodule (wiring to the provider).
shared¶
Utility modules consumed by features:
| Module | Purpose |
|---|---|
altitude |
Altitude gain calculator |
ble |
BLE primitives (GATT, scanning, state, permissions) |
concurrent |
Coroutine dispatchers, suspendRunCatching, ConcurrentFactory |
design-system |
Material 3 theme, colors, spacing, components |
di |
Hilt qualifier annotations |
distance |
Distance calculator |
error |
Error mapping utilities |
graphics |
Bitmap utilities |
id |
ID generator |
location |
Location services, validation, smoothing |
map |
Google Maps route rendering constants & utilities |
sensor-protocol |
BLE sensor data parsing (cycling power measurement) |
testing |
Shared test utilities (MainDispatcherRule, etc) |
Module Dependency Graph¶
The full dependency graph is auto-generated by CI on each push to main. View the standalone page: Module Graph.
%%{
init: {
'theme': 'neutral'
}
}%%
graph LR
subgraph :feature
:feature:nutrition["nutrition"]
:feature:profile["profile"]
:feature:connections["connections"]
:feature:poi["poi"]
:feature:settings["settings"]
:feature:theme["theme"]
:feature:locale["locale"]
:feature:dashboard["dashboard"]
:feature:destinations["destinations"]
:feature:session["session"]
end
subgraph :feature:bridge:connection-session
:feature:bridge:connection-session:api["api"]
:feature:bridge:connection-session:impl["impl"]
end
subgraph :feature:bridge:destination-nutrition
:feature:bridge:destination-nutrition:impl["impl"]
:feature:bridge:destination-nutrition:api["api"]
end
subgraph :feature:bridge:destination-poi
:feature:bridge:destination-poi:impl["impl"]
:feature:bridge:destination-poi:api["api"]
end
subgraph :feature:bridge:destination-session
:feature:bridge:destination-session:api["api"]
:feature:bridge:destination-session:impl["impl"]
end
subgraph :feature:bridge:nutrition-session
:feature:bridge:nutrition-session:api["api"]
:feature:bridge:nutrition-session:impl["impl"]
end
subgraph :feature:bridge:nutrition-settings
:feature:bridge:nutrition-settings:api["api"]
:feature:bridge:nutrition-settings:impl["impl"]
end
subgraph :feature:bridge:poi-settings
:feature:bridge:poi-settings:api["api"]
:feature:bridge:poi-settings:impl["impl"]
end
subgraph :feature:bridge:profile-session
:feature:bridge:profile-session:impl["impl"]
:feature:bridge:profile-session:api["api"]
end
subgraph :feature:bridge:session-settings
:feature:bridge:session-settings:api["api"]
:feature:bridge:session-settings:impl["impl"]
end
subgraph :feature:sensor
:feature:sensor:power["power"]
end
subgraph :shared
:shared:ble["ble"]
:shared:concurrent["concurrent"]
:shared:design-system["design-system"]
:shared:di["di"]
:shared:error["error"]
:shared:id["id"]
:shared:sensor-protocol["sensor-protocol"]
:shared:altitude["altitude"]
:shared:distance["distance"]
:shared:location["location"]
:shared:map["map"]
:shared:observability["observability"]
:shared:graphics["graphics"]
end
:feature:bridge:destination-nutrition:impl --> :feature:bridge:destination-nutrition:api
:feature:bridge:destination-nutrition:impl --> :feature:nutrition
:feature:bridge:profile-session:impl --> :feature:bridge:profile-session:api
:feature:bridge:profile-session:impl --> :feature:profile
:feature:connections --> :feature:sensor:power
:feature:connections --> :shared:ble
:feature:connections --> :shared:concurrent
:feature:connections --> :shared:design-system
:feature:connections --> :shared:di
:feature:connections --> :shared:error
:feature:connections --> :shared:id
:feature:connections --> :shared:sensor-protocol
:feature:bridge:destination-poi:impl --> :feature:bridge:destination-poi:api
:feature:bridge:destination-poi:impl --> :feature:poi
:feature:settings --> :feature:theme
:feature:settings --> :feature:locale
:feature:settings --> :feature:profile
:feature:settings --> :feature:bridge:nutrition-settings:api
:feature:settings --> :feature:bridge:poi-settings:api
:feature:settings --> :feature:bridge:session-settings:api
:feature:settings --> :shared:concurrent
:feature:settings --> :shared:design-system
:feature:settings --> :shared:di
:app --> :baselineprofile
:app --> :feature:connections
:app --> :feature:bridge:connection-session:api
:app --> :feature:bridge:connection-session:impl
:app --> :feature:bridge:destination-nutrition:api
:app --> :feature:bridge:destination-nutrition:impl
:app --> :feature:bridge:destination-poi:api
:app --> :feature:bridge:destination-poi:impl
:app --> :feature:bridge:destination-session:api
:app --> :feature:bridge:destination-session:impl
:app --> :feature:bridge:nutrition-session:api
:app --> :feature:bridge:nutrition-session:impl
:app --> :feature:bridge:nutrition-settings:api
:app --> :feature:bridge:nutrition-settings:impl
:app --> :feature:bridge:poi-settings:api
:app --> :feature:bridge:poi-settings:impl
:app --> :feature:bridge:profile-session:api
:app --> :feature:bridge:profile-session:impl
:app --> :feature:bridge:session-settings:api
:app --> :feature:bridge:session-settings:impl
:app --> :feature:dashboard
:app --> :feature:destinations
:app --> :feature:locale
:app --> :feature:nutrition
:app --> :feature:poi
:app --> :feature:profile
:app --> :feature:sensor:power
:app --> :feature:session
:app --> :feature:settings
:app --> :feature:theme
:app --> :shared:altitude
:app --> :shared:ble
:app --> :shared:concurrent
:app --> :shared:design-system
:app --> :shared:di
:app --> :shared:distance
:app --> :shared:id
:app --> :shared:location
:app --> :shared:error
:app --> :shared:map
:app --> :shared:observability
:app --> :shared:sensor-protocol
:feature:bridge:destination-session:api --> :shared:location
:feature:session --> :shared:altitude
:feature:session --> :shared:concurrent
:feature:session --> :shared:design-system
:feature:session --> :shared:di
:feature:session --> :shared:distance
:feature:session --> :shared:error
:feature:session --> :shared:graphics
:feature:session --> :shared:id
:feature:session --> :shared:map
:feature:session --> :shared:location
:feature:session --> :feature:bridge:connection-session:api
:feature:session --> :feature:bridge:nutrition-session:api
:feature:session --> :feature:bridge:profile-session:api
:feature:session --> :feature:theme
:feature:dashboard --> :feature:destinations
:feature:dashboard --> :feature:bridge:destination-session:api
:feature:dashboard --> :shared:design-system
:feature:nutrition --> :feature:bridge:nutrition-session:api
:feature:nutrition --> :shared:concurrent
:feature:nutrition --> :shared:design-system
:feature:nutrition --> :shared:di
:shared:map --> :shared:graphics
:feature:bridge:destination-session:impl --> :feature:bridge:destination-session:api
:feature:bridge:destination-session:impl --> :feature:session
:feature:bridge:destination-session:impl --> :shared:concurrent
:feature:bridge:destination-session:impl --> :shared:location
:shared:concurrent --> :shared:di
:feature:bridge:connection-session:impl --> :feature:bridge:connection-session:api
:feature:bridge:connection-session:impl --> :feature:connections
:feature:bridge:connection-session:impl --> :feature:sensor:power
:feature:bridge:nutrition-session:impl --> :feature:nutrition
:feature:bridge:nutrition-session:impl --> :feature:bridge:nutrition-session:api
:feature:bridge:nutrition-session:impl --> :feature:session
:feature:bridge:session-settings:impl --> :feature:bridge:session-settings:api
:feature:bridge:session-settings:impl --> :feature:session
:shared:observability --> :shared:concurrent
:shared:error --> :shared:concurrent
:shared:error --> :shared:design-system
:shared:error --> :shared:di
:feature:sensor:power --> :shared:ble
:feature:sensor:power --> :shared:concurrent
:feature:sensor:power --> :shared:design-system
:feature:sensor:power --> :shared:di
:feature:sensor:power --> :shared:error
:feature:sensor:power --> :shared:sensor-protocol
:feature:theme --> :shared:concurrent
:feature:theme --> :shared:di
:baselineprofile --> :shared:design-system
:baselineprofile --> :app
:shared:ble --> :shared:concurrent
:feature:locale --> :shared:concurrent
:feature:locale --> :shared:di
:feature:bridge:nutrition-settings:impl --> :feature:bridge:nutrition-settings:api
:feature:bridge:nutrition-settings:impl --> :feature:nutrition
:feature:bridge:poi-settings:impl --> :feature:bridge:poi-settings:api
:feature:bridge:poi-settings:impl --> :feature:poi
:shared:location --> :shared:concurrent
:shared:location --> :shared:di
:feature:destinations --> :feature:bridge:destination-nutrition:api
:feature:destinations --> :feature:bridge:destination-poi:api
:feature:destinations --> :feature:bridge:destination-session:api
:feature:destinations --> :shared:concurrent
:feature:destinations --> :shared:design-system
:feature:destinations --> :shared:di
:feature:destinations --> :shared:distance
:feature:destinations --> :shared:graphics
:feature:destinations --> :shared:location
:feature:destinations --> :shared:map
:feature:profile --> :shared:concurrent
:feature:profile --> :shared:di
:feature:poi --> :shared:concurrent
:feature:poi --> :shared:design-system
:feature:poi --> :shared:di