Model View ViewModel
MVVM Architecture
MVVM (Model-View-ViewModel) is a software architectural pattern intended to facilitate the separation of the development of the graphical user interface (the View) from the development of the business logic or back-end logic (the Model). The ViewModel serves as the intermediary component that handles the communication between the Model and the View. This separation of concerns helps manage complexity, improve testability, and facilitate easier maintenance of applications containing complex user interfaces. It is widely adopted in modern client-side development, particularly in Android (supported by Jetpack libraries), iOS (via SwiftUI or various frameworks), and Windows development (WPF, UWP).
Core Components
MVVM breaks down the application into three distinct layers with strict dependency rules.

Model
The Model represents the data domain and the business logic of the application. It is the single source of truth for data.
- Responsibilities: It holds the raw data structures, performs data validation, and manages data retrieval and storage from various sources like local databases (e.g., Room/SQLite), network APIs (e.g., REST/GraphQL), or repositories.
- Knowledge: The Model is completely ignorant of the ViewModel and the View. It has no dependencies on UI components.
View
The View is the visual layer that the user interacts with. It is responsible for the structure, layout, and appearance of what users see on the screen.
- Responsibilities: It renders the UI elements and captures user input events (like clicks, gestures, or text input). The View should be "passive" or "dumb," meaning it should contain zero business logic. Its only job is to display whatever state the ViewModel provides and forward user actions to the ViewModel.
- Knowledge: The View knows about the ViewModel. It subscribes to or observes changes in the ViewModel's state. In Android, the View is typically an `Activity`, `Fragment`, or Jetpack Compose functions.
ViewModel
The ViewModel acts as the bridge or glue between the View and the Model. It is an abstraction of the View exposing public properties and commands.
- Responsibilities: It retrieves data from the Model, processes it into a format ready for display by the View, and holds the current state of the UI. It also handles user actions forwarded by the View, translating them into operations on the Model.
- Knowledge: The ViewModel knows about the Model. Crucially, the ViewModel does not know about the View. It holds no reference to UI components (like TextViews or Buttons) or platform-specific UI contexts (like an Android `Context`). This independence ensures that the ViewModel can be unit-tested in isolation.
The Data Flow Mechanism
The power of MVVM lies in its specific directional data flow, achieved through an "observer pattern."
1. User Action: The user interacts with the View (e.g., presses a button).
2. Command forwarding: The View calls a method on the ViewModel to handle this action.
3. Model interaction: The ViewModel processes the action and, if necessary, requests updates or new data from the Model.
4. Data update: The Model performs the business logic and updates the data.
5. State change notification: The ViewModel receives the updated data from the Model and updates its own internal state. Because the ViewModel's state properties are "observable" (using mechanisms like Kotlin `StateFlow`), the ViewModel automatically notifies any subscribers that its state has changed.
6. UI Refresh: The View, which is observing the ViewModel, receives the notification and automatically re-renders itself to reflect the new state.
Benefits of MVVM
- Separation of concerns: By decoupling UI logic from business logic, the codebase becomes cleaner and easier to navigate.
- Testability: Because the ViewModel has no dependency on the View or platform-specific UI frameworks, business logic hosted in the ViewModel can be easily unit-tested with standard testing frameworks like JUnit.
- Maintainability: UI changes can be made in the View without affecting the ViewModel or Model, and changes to business rules in the Model rarely affect the View directly.
- Handling lifecycle events: In frameworks like Android, ViewModels are designed to survive configuration changes (like screen rotations), ensuring data isn't lost when the UI is recreated.
Kotlin example implementation
Below is a simplified example of a counter application in Kotlin for Android, demonstrating the three layers.
The Model (Data Layer)
A simple repository that holds the count value. In a real app, this might interact with a database.
class CounterRepository {
private var count = 0
fun getCurrentCount(): Int {
return count
}
fun incrementCount(): Int {
count++
return count
}
}The ViewModel
The ViewModel holds the UI state using `MutableStateFlow`, which is a modern, observable data holder in Kotlin coroutines. It exposes an immutable `StateFlow` to the View.
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
// The ViewModel inherits from the framework's base ViewModel class
class CounterViewModel(private val repository: CounterRepository = CounterRepository()) : ViewModel() {
// Mutable internal state
private val _uiState = MutableStateFlow(0)
// Immutable exposed state for the View to observe
val uiState: StateFlow<Int> = _uiState.asStateFlow()
init {
// Initialize state from the model
_uiState.value = repository.getCurrentCount()
}
// Method called by the View upon user interaction
fun onIncrementButtonPressed() {
val newCount = repository.incrementCount()
// Update the state; observers will be notified automatically
_uiState.value = newCount
}
}The View (UI Layer)
A simple Android Activity that sets up the UI elements and observes the ViewModel's state. Note that the actual observation requires a coroutine scope (omitted here for brevity, but essential in practice).
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
// Imports for ViewModel delegation and lifecycle scope would be here
class CounterActivity : AppCompatActivity() {
// Obtain an instance of the ViewModel
private val viewModel: CounterViewModel by viewModels()
private lateinit var countTextView: TextView
private lateinit var incrementButton: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_counter)
// 1. Initialize UI elements
countTextView = findViewById(R.id.countTextView)
incrementButton = findViewById(R.id.incrementButton)
// 2. Handle User Actions (View -> ViewModel)
incrementButton.setOnClickListener {
viewModel.onIncrementButtonPressed()
}
// 3. Observe ViewModel State (ViewModel -> View)
// Note: In a real Android app, launch this in lifecycleScope.launch {}
viewModel.uiState.collect { count ->
// Update the UI whenever the state changes
countTextView.text = count.toString()
}
}
}