← All courses

App Architecture: MVVM, Repository & Flow

🗓 May 31, 2026 ⏱ 2 min read

Why architecture matters

When you put networking, database and UI code all inside an Activity, the file becomes huge, bugs multiply, and data is lost on rotation. A clear architecture splits responsibilities so each part has one job. Google recommends MVVM with a Repository.

UI (View) ViewModel Repository Data
  • View (Activity/Fragment/Compose) — only displays state and forwards user events.
  • ViewModel — holds UI state and logic; survives rotation.
  • Repository — the single source of truth; decides whether data comes from network or database.
  • Data sources — Retrofit (network) and Room (local).

The Repository

class UserRepository(
    private val api: ApiService,
    private val dao: UserDao
) {
    fun observeUsers(): Flow<List<User>> = dao.observeAll()

    suspend fun refresh() {
        val fresh = api.getUsers()   // from network
        dao.insertAll(fresh)         // cache locally
    }
}

The ViewModel exposes a single state

class UsersViewModel(private val repo: UserRepository) : ViewModel() {
    val users: StateFlow<List<User>> = repo.observeUsers()
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())

    fun refresh() = viewModelScope.launch { repo.refresh() }
}

The UI just observes

lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.users.collect { list -> adapter.submitList(list) }
    }
}

Common mistakes

  • Calling the network directly from the Activity — route it through ViewModel → Repository.
  • Exposing mutable state — expose read-only StateFlow, keep the mutable one private.
  • Collecting flows without repeatOnLifecycle — it wastes resources when the screen is hidden.
Summary: View → ViewModel → Repository → (Retrofit + Room). One job per layer makes apps testable, rotation-safe, and easy to grow.