Architecture layers
The app is organized into three main layers:Domain layer
Business logic, alarm state machine, and core interfaces
Data layer
Persistence with DataStore, repositories, and data models
UI layer
ViewModels, activities, fragments, and UI state management
Dependency injection with Koin
The app uses Koin for dependency injection, configured inContainer.kt:
Container.kt
The
Container.kt file defines all dependencies and their lifecycles. Single instances are created once and reused, while factories create new instances on demand.Key architectural patterns
State machine
TheAlarmCore class implements a state machine to manage alarm lifecycle:
- DisabledState: Alarm is turned off
- EnabledState: Alarm is active with substates:
- SetState: Waiting to fire (NormalSetState or PreAlarmSetState)
- FiredState: Alarm is ringing
- SnoozedState: Alarm is snoozed
- SkippingSetState: Alarm is skipped for one occurrence
Active record pattern
The data layer uses an active record pattern whereAlarmStore instances represent individual alarms and handle their own persistence:
Reactive streams
The app uses RxJava for reactive data flows:Store.kt
Dependency flow
The architecture ensures:- UI layer depends on domain interfaces, not implementations
- Domain layer contains business logic and is independent of Android framework
- Data layer handles persistence and is abstracted behind repository interfaces
- Reactive streams enable decoupled communication between components
Module organization
The source code is organized by layer:com.better.alarm.domain/- Domain logic (Alarms, AlarmCore, interfaces)com.better.alarm.data/- Data models and persistence (AlarmValue, AlarmsRepository)com.better.alarm.ui/- UI components (ViewModels, Activities, Fragments)com.better.alarm.bootstrap/- Dependency injection setup (Container)com.better.alarm.services/- Background services for alarm schedulingcom.better.alarm.receivers/- Broadcast receivers for system events