Compose Architecture Review
When to Use This Skill:
- Reviewing EXISTING Compose screens/ViewModels
- Code review for architectural violations
- Refactoring guidance for specific files
- Validating state management patterns
When to Use
project-architecture-analyst-androidAgent Instead:
- Planning NEW features (uncertain file placement)
- Understanding project-wide architecture
- Major changes affecting multiple modules
- Need to discover existing patterns before implementing
Review Checklist
Separation of Concerns
- Composables: presentation only (no business logic, network, DB)
- ViewModels: state + business logic (StateFlow, events)
- Repository/UseCase: data access separated from UI
State Management
remember- composable-local state onlyrememberSaveable- survive config changesViewModelwithStateFlow/MutableStateFlow- screen statecollectAsStateWithLifecycle()- lifecycle-aware collectionderivedStateOf- computed state from other state- CompositionLocal - shared read-only state
- Use unidirectional data flow: state flows down, events flow up
- Prefer stateless composables (receive value + onChange callbacks)
- Use plain state-holder classes for complex state not tied to lifecycle
- Prefer side effects via
LaunchedEffect(async work),DisposableEffect(cleanup),SideEffect(framework integration),produceState,rememberCoroutineScope, orsnapshotFlowrather than arbitrary side effects in composable bodies - Use
SavedStateHandlefor persistent state across process death
State Lifecycle Issues
- No leaked ViewModels (use viewModel() or hiltViewModel())
- Proper cleanup in ViewModel.onCleared()
- StateFlow collected with lifecycle awareness
- No unnecessary state hoisting
Dependency Injection
- Dependencies injected (check project: Koin, Hilt, Dagger, or manual)
- No hardcoded singletons in composables
- ViewModels receive dependencies via constructor
Composable Design
- Composables are small (single responsibility)
- Complex screens broken into components
- Reusable components extracted
- Stateless composables preferred (state hoisting)
Common Issues
| Issue | Fix |
|---|---|
| Business logic in composable | Extract to ViewModel |
| Network call in composable | Move to ViewModel with LaunchedEffect |
| Singleton in composable | Inject via DI or CompositionLocal |
| 200+ line composable | Break into smaller components |
| State not hoisted | Hoist to parent or ViewModel |
| Side effects or network calls directly in composable | Wrap in LaunchedEffect(key) { ... } or move to ViewModel |
| Stateful composable when stateless would suffice | Hoist state and pass value + lambda |
State Hoisting Pattern
Composable receives: value: T, onValueChange: (T) -> Unit
ViewModel holds: MutableStateFlow<T>
Severity
- 🔴 Critical: Architecture violation
- 🟡 Improvement: Better pattern available
- 🟢 Enhancement: Optional optimization