MVVM Design Pattern
MVVM (Model-View-ViewModel) is an architectural pattern that separates an application into three main components, promoting separation of concerns and testability.
- View
- ViewModel
- Model
graph TB
subgraph "MVVM Architecture"
V[View - UI Layer]
VM[ViewModel - Business Logic]
M[Model - Data Layer]
end
V -- Observes --> VM
VM -- Updates --> V
VM -- Uses --> M
M -- Notifies --> VM
Core Components
Section titled “Core Components”1. Model
Section titled “1. Model”
classDiagram
class Model {
+data: Object
+businessLogic()
+dataOperations()
}
class DataService {
+fetchData()
+saveData()
}
Model --> DataService : uses
|
Responsibility: Represents data and business logic
|
2. View
Section titled “2. View”
classDiagram
class View {
+UIComponents
+bindToViewModel()
+handleUserInput()
+updateUI()
}
class ViewModel {
+exposedProperties
+commands
}
View --> ViewModel : observes
|
Responsibility: Handles UI presentation
|
3. ViewModel
Section titled “3. ViewModel”
classDiagram
class ViewModel {
+Model model
+exposeData()
+handleCommands()
+notifyView()
}
class Model {
+rawData
}
ViewModel --> Model : transforms
ViewModel --|> INotifyPropertyChanged : implements
|
Responsibility: Mediator between View and Model
|
Data Flow
Section titled “Data Flow”sequenceDiagram
participant V as View
participant VM as ViewModel
participant M as Model
V->>VM: User action/command
VM->>M: Request data/operation
M->>VM: Return data/result
VM->>VM: Transform data for UI
VM->>V: Update observable properties
V->>V: Refresh UI automatically
Implementation in Mobile Platforms
Section titled “Implementation in Mobile Platforms”Android (Jetpack Compose + ViewModel)
Section titled “Android (Jetpack Compose + ViewModel)”// Modeldata class User(val id: Int, val name: String, val email: String)
// Repositoryclass UserRepository { suspend fun getUser(): User { // API call or database operation return User(1, "John", "john@email.com") }}
// ViewModelclass UserViewModel : ViewModel() { private val repository = UserRepository() var userState by mutableStateOf<User?>(null) private set
fun loadUser() { viewModelScope.launch { userState = repository.getUser() } }}
// View@Composablefun UserScreen(viewModel: UserViewModel = hiltViewModel()) { val user by viewModel.userState
LaunchedEffect(Unit) { viewModel.loadUser() }
user?.let { user -> Text(text = user.name) Text(text = user.email) }}iOS (SwiftUI + Combine)
Section titled “iOS (SwiftUI + Combine)”// Modelstruct User: Identifiable, Codable { let id: Int let name: String let email: String}
// Serviceclass UserService { func fetchUser() async throws -> User { // API call implementation return User(id: 1, name: "John", email: "john@email.com") }}
// ViewModel@MainActorclass UserViewModel: ObservableObject { @Published var user: User? private let service = UserService()
func loadUser() async { do { user = try await service.fetchUser() } catch { print("Error loading user: \(error)") } }}
// Viewstruct UserView: View { @StateObject private var viewModel = UserViewModel()
var body: some View { VStack { if let user = viewModel.user { Text(user.name) Text(user.email) } } .task { await viewModel.loadUser() } }}Flutter (Provider/Bloc/Riverpod)
Section titled “Flutter (Provider/Bloc/Riverpod)”// Modelclass User { final int id; final String name; final String email;
User({required this.id, required this.name, required this.email});}
// Repositoryclass UserRepository { Future<User> getUser() async { // API call implementation return User(id: 1, name: 'John', email: 'john@email.com'); }}
// ViewModel (using ChangeNotifier)class UserViewModel with ChangeNotifier { final UserRepository _repository = UserRepository(); User? _user;
User? get user => _user;
Future<void> loadUser() async { _user = await _repository.getUser(); notifyListeners(); // Notify View to rebuild }}
// Viewclass UserScreen extends StatelessWidget { @override Widget build(BuildContext context) { final viewModel = Provider.of<UserViewModel>(context);
return FutureBuilder( future: viewModel.loadUser(), builder: (context, snapshot) { if (viewModel.user != null) { return Column( children: [ Text(viewModel.user!.name), Text(viewModel.user!.email), ], ); } return CircularProgressIndicator(); }, ); }}Benefits of MVVM
Section titled “Benefits of MVVM”- Separation of Concerns: Clear separation between UI, business logic, and data
- Testability: ViewModel can be tested without UI dependencies
- Maintainability: Easier to modify and extend
- Data Binding: Reduces boilerplate code for UI updates
- Team Collaboration: Different team members can work on different components
Common Data Binding Mechanisms
Section titled “Common Data Binding Mechanisms”- Android: LiveData, StateFlow, Observable fields
- iOS: @Published, Combine framework, ObservableObject
- Flutter: Provider, Riverpod, Bloc, ValueNotifier
- Cross-platform: RxJava/RxSwift, MobX
MVVM is particularly well-suited for mobile development due to its ability to handle complex UI state management while maintaining clean separation between components.