skills/flutter-architecture/references/mvvm.md

120 lines
3.6 KiB
Markdown

# MVVM Pattern
Model-View-ViewModel architectural pattern for Flutter applications.
## Overview
MVVM separates application features into three parts:
- **Model**: Data and business logic (Repositories, Services)
- **View**: UI presentation (Widgets)
- **ViewModel**: UI logic and state management
## Component Relationships
Every screen or user flow usually contains:
- One **View** (UI)
- One **ViewModel** (UI logic)
- One or more **Repositories** (data sources)
- Zero or more **Services** (external API access)
- Zero or more **Use-cases** for complex or reused business logic
Views and ViewModels normally have a **one-to-one relationship** at the screen
or flow level. A View may be composed of many smaller widgets.
## View Layer
### Views (Widgets)
- Compose widgets to display UI
- Pass events to ViewModel via commands
- Receive data from ViewModel
- Contain minimal logic:
- Simple if-statements for conditional rendering
- Animation logic
- Layout logic based on device info
- Simple routing logic
**Important**: A View is not a single widget. Views are collections of widgets. One view may contain many widgets. ViewModels have one-to-one relationship with views, not individual widgets.
### ViewModels
- Transform repository data into UI state
- Maintain current UI state for rebuilds
- Expose commands (callback functions) for user actions
- Hold state that survives configuration changes
Responsibilities:
- Retrieve application data from repositories
- Filter, sort, aggregate data for presentation
- Track UI state (flags, carousel positions, etc.)
- Expose commands for button presses, form submissions, etc.
- Call repositories directly for simple operations, or call use-cases when the
logic is complex, reused, or spans multiple repositories.
## Model Layer
### Repositories
Single source of truth for model data. Each data type has one repository class.
Responsibilities:
- Poll data from services
- Transform raw data into domain models
- Handle business logic:
- Caching
- Error handling
- Retry logic
- Refreshing data (polling, user actions)
Output: Domain models as Streams or Futures
Relationships:
- Many-to-many with ViewModels
- One ViewModel can use multiple Repositories
- One Repository can be used by multiple ViewModels
- Repositories should never be aware of each other
### Use-cases (Optional)
Use-cases sit between ViewModels and Repositories only when they reduce
duplication or isolate complex business rules. Do not add them to simple CRUD
flows just to fill a layer.
### Services
Lowest layer, wrap API endpoints and expose async response objects.
Responsibilities:
- Isolate data-loading
- Stateless (no state held)
- One service per data source
Examples:
- Platform APIs (iOS, Android)
- REST endpoints
- Local files
- Databases
Relationships:
- Many-to-many with Repositories
- One Repository can use multiple Services
- One Service can be used by multiple Repositories
## Data Flow
### User Interaction Flow
1. View: User interaction triggers event
2. View: Event handler calls ViewModel command
3. ViewModel: Command calls Repository directly, or a Use-case when justified
4. Repository: Updates data and returns new data
5. ViewModel: Saves new state
6. View: UI rebuilds with new state
### Data-Originated Flow
1. Repository: Polls service for new data
2. Repository: Updates data
3. ViewModel: Receives new data from Repository
4. View: UI rebuilds with new state
## Benefits
- **Testability**: Test ViewModel logic by mocking Repositories
- **Maintainability**: Clear separation of concerns
- **Scalability**: Easy to add features without breaking existing code
- **Reusability**: Components have well-defined interfaces