Modern Architecture Patterns
1. MVVM Architecture
Q: Explain MVVM architecture and why we use it in Android.
MVVM (Model-View-ViewModel) separates an application into three layers:
| Layer | Responsibility | Android Components |
|---|---|---|
| Model | Data and business logic | Repository, Data Sources, Room, Retrofit |
| View | Display UI, capture user input | Activity, Fragment, Composable |
| ViewModel | Hold UI state, expose data to View | Jetpack ViewModel |
Q: Why is MVVM the recommended pattern?
- Separation of Concerns: Each layer has a single responsibility
- Testability: ViewModel can be unit tested without Android framework
- Lifecycle Awareness: ViewModel survives configuration changes
- Maintainability: Changes in one layer don’t cascade to others
Q: What are common MVVM mistakes?
- Passing Activity/Fragment to ViewModel: Causes memory leaks
- Holding View references in ViewModel: Same leak issue
- Business logic in View: Should be in ViewModel or Use Cases
- ViewModel importing android. packages*: Breaks testability (lifecycle components excepted)
Q: What’s the Single UI State pattern?
Instead of multiple LiveData/StateFlow for different UI elements, use one sealed class representing all possible screen states. This prevents impossible state combinations and makes reasoning about UI easier.
2. MVI Architecture
Q: Explain MVI and when to use it over MVVM.
MVI (Model-View-Intent) enforces unidirectional data flow:
- Intent: User actions or events (ButtonClicked, TextChanged)
- Model: Complete, immutable UI state at any moment
- View: Renders state, emits intents
The flow is circular: User Action → Intent → Reducer → New State → View Update
Q: MVVM vs MVI comparison?
| Aspect | MVVM | MVI |
|---|---|---|
| State | Multiple StateFlows | Single ViewState |
| Complexity | Lower | Higher boilerplate |
| Predictability | Good | Excellent (single source of truth) |
| Debugging | Moderate | Easy (log all state changes) |
| Best For | Simple-medium apps | Complex interactions, forms |
Q: When to use MVI?
- Complex multi-step workflows
- Forms with interdependent validation
- Need to track/debug all state changes
- Time-travel debugging requirements
- State consistency is critical (financial, healthcare apps)
3. Google’s Recommended Architecture
Q: Describe the layered architecture Google recommends.
Three layers with clear dependencies (outer layers depend on inner):
UI Layer (Presentation)
- UI elements: Activities, Fragments, Composables
- State holders: ViewModels
- Observes state, forwards events
Domain Layer (Optional)
- Use Cases / Interactors
- Contains reusable business logic
- Combines data from multiple repositories
Data Layer
- Repositories: Single source of truth for each data type
- Data Sources: Remote (API), Local (Room), Cache
Q: What are the key principles?
- Separation of Concerns: UI shouldn’t know about data sources
- Drive UI from Data Models: UI reflects state, doesn’t own it
- Single Source of Truth (SSOT): One owner per data type
- Unidirectional Data Flow: State flows down, events flow up
Q: When is the Domain layer needed?
- Business logic reused across multiple ViewModels
- Complex data transformations or combinations
- Need to enforce business rules consistently
- Large teams where clear boundaries help
4. Repository Pattern
Q: What is the Repository pattern and why use it?
Repository is an abstraction over data sources. It:
- Decides whether to fetch from network or cache
- Handles caching strategies
- Transforms DTOs to domain models
- Provides a clean API to the rest of the app
Q: What caching strategies are common?
| Strategy | Behavior | Use Case |
|---|---|---|
| Cache First | Return cache, then refresh | News feeds, social content |
| Network First | Try network, fallback to cache | Critical data, payments |
| Cache Only | Never hit network | Offline-first apps |
| Network Only | Never cache | Sensitive data |
Q: How do you expose data from Repository?
Use Flow for observable data streams. The Room DAO returns Flow, Repository exposes it, ViewModel collects it. Changes propagate automatically from database to UI.
5. Clean Architecture
Q: How does Clean Architecture relate to Android?
Clean Architecture organizes code in concentric circles:
- Entities (innermost): Business objects, pure Kotlin
- Use Cases: Application-specific business rules
- Interface Adapters: ViewModels, Repositories, Presenters
- Frameworks (outermost): Android, Room, Retrofit
The Dependency Rule: Dependencies point inward. Inner layers know nothing about outer layers.
Q: What are the benefits?
- Business logic is completely isolated from frameworks
- Easy to test core logic without Android dependencies
- Can swap frameworks (e.g., Retrofit → Ktor) without touching business logic
- Forces clear separation and single responsibility
Q: What are the drawbacks?
- More boilerplate (mappers, interfaces, multiple models)
- Can be overkill for simple apps
- Learning curve for the team
- Need discipline to maintain boundaries
6. State Management
Q: LiveData vs StateFlow vs SharedFlow?
| Type | Hot/Cold | Lifecycle Aware | Initial Value | Replay |
|---|---|---|---|---|
| LiveData | Hot | Yes (automatic) | Optional | Latest |
| StateFlow | Hot | No (need repeatOnLifecycle) | Required | Latest |
| SharedFlow | Hot | No | Not required | Configurable |
Q: When to use each?
- LiveData: Simple cases, team familiar with it, automatic lifecycle handling
- StateFlow: UI state, need
.valueaccess, Compose integration - SharedFlow: One-time events (navigation, toasts), multiple subscribers
Q: How do you handle one-time events in MVVM?
Options:
- Channel + receiveAsFlow(): Consumed exactly once
- SharedFlow with replay=0: Events don’t replay to new subscribers
- Event wrapper class: Wrap value, mark as consumed
Avoid using StateFlow for events—new subscribers receive the last event again.
7. Dependency Injection in Architecture
Q: How does DI fit into the architecture?
DI provides dependencies to each layer:
- ViewModel receives Repository and Use Cases
- Repository receives DataSources
- DataSources receive Retrofit, Room, etc.
This enables:
- Swapping implementations (real vs fake for testing)
- Scoping (singleton, per-screen, per-ViewModel)
- Lazy initialization
Q: What scopes are common in Android DI?
| Scope | Lifecycle | Example |
|---|---|---|
| Singleton | App lifetime | Retrofit, OkHttp, Room Database |
| Activity/Fragment | Screen lifetime | Screen-specific analytics |
| ViewModel | ViewModel lifetime | Use Cases with state |
Quick Reference
| Pattern | Key Insight |
|---|---|
| MVVM | ViewModel holds UI state, survives config changes, never references View |
| MVI | Single immutable state, unidirectional flow, great for complex UIs |
| Layered Architecture | UI → Domain (optional) → Data; dependencies point inward |
| Repository | Abstracts data sources, handles caching, single source of truth |
| Clean Architecture | Business logic independent of frameworks; more structure, more boilerplate |
| State Management | StateFlow for state, Channel/SharedFlow for events, avoid LiveData for new code |