Skip to main content

Overview

Divvy implements the MVVM (Model-View-ViewModel) pattern to separate business logic from UI code, making the app more testable, maintainable, and scalable. With Jetpack Compose, this pattern integrates seamlessly through state management and reactive UI updates.

MVVM Components

Model

Domain models and repositories that represent data and business logic

View

Composable functions that display UI and handle user interactions

ViewModel

Manages UI state and handles business logic between View and Model

Directory Structure

Each feature follows a consistent structure:

State Management

Divvy uses Kotlin StateFlow for reactive state management:
StateFlow is used instead of LiveData for better Compose integration and Kotlin coroutines support.

Example: Groups Feature

Let’s examine the Groups feature to see MVVM in action.

Model Layer

Domain Model

app/src/main/java/com/example/divvy/models/Group.kt

Repository Interface

app/src/main/java/com/example/divvy/backend/GroupRepository.kt

Repository Implementation

app/src/main/java/com/example/divvy/backend/SupabaseGroupRepository.kt

ViewModel Layer

app/src/main/java/com/example/divvy/ui/groups/ViewModels/GroupsViewModel.kt
The @HiltViewModel annotation enables Hilt to inject dependencies automatically. ViewModels are scoped to the navigation destination lifecycle.

View Layer

app/src/main/java/com/example/divvy/ui/groups/Views/GroupsScreen.kt

Key Patterns

Repository Injection via Hilt

Hilt automatically provides repository instances configured in AppModule.kt.

State Updates with StateFlow

Using .update { } ensures atomic state updates and prevents race conditions.

Collecting State in Compose

Compose automatically recomposes when uiState changes.

Handling Side Effects

LaunchedEffect handles one-time events like navigation while keeping the ViewModel logic clean.

Data Flow Diagram

Testing Benefits

The MVVM pattern makes testing easier:

Best Practices

1

Single Responsibility

Each ViewModel manages state for one screen or feature
2

Immutable State

Use data class with copy() for immutable state updates
3

Coroutine Scope

Always use viewModelScope for coroutines - they’re automatically cancelled
4

Error Handling

Include error states in your UI state and handle them in the View
5

Loading States

Always show loading indicators during async operations
Never reference Android Context or View classes directly in ViewModels. This creates memory leaks and makes testing difficult.

Next Steps

Navigation

Learn how navigation works with MVVM

Architecture Overview

Return to architecture overview