Architecture & Design Patterns
Phần này tổng hợp các câu hỏi về Architecture patterns và Design principles trong Android development.
1. MVVM (Model-View-ViewModel)
Phần tiêu đề “1. MVVM (Model-View-ViewModel)”Q: MVVM pattern là gì? Giải thích từng layer?
Phần tiêu đề “Q: MVVM pattern là gì? Giải thích từng layer?”Trả lời:
MVVM là architecture pattern tách biệt UI logic khỏi business logic:
flowchart LR V["View<br/>(Activity/Fragment/<br/>Composable)"] <--> VM["ViewModel<br/>(UI State +<br/>Business Logic)"] VM <--> M["Model<br/>(Repository/<br/>Data Sources)"]
style V fill:#e3f2fd style VM fill:#fff3e0 style M fill:#e8f5e9| Layer | Responsibility | Android Components |
|---|---|---|
| Model | Data + business logic | Repository, Room, Retrofit |
| View | Display UI, handle input | Activity, Fragment, Composable |
| ViewModel | Hold UI state, transform data | ViewModel class |
// Model (Repository)class UserRepository(private val api: UserApi) { suspend fun getUser(id: String): User = api.fetchUser(id)}
// ViewModelclass UserViewModel(private val repository: UserRepository) : ViewModel() {
private val _uiState = MutableStateFlow<UiState>(UiState.Loading) val uiState: StateFlow<UiState> = _uiState.asStateFlow()
fun loadUser(id: String) { viewModelScope.launch { _uiState.value = UiState.Loading try { val user = repository.getUser(id) _uiState.value = UiState.Success(user) } catch (e: Exception) { _uiState.value = UiState.Error(e.message) } } }}
// View (Compose)@Composablefun UserScreen(viewModel: UserViewModel) { val uiState by viewModel.uiState.collectAsStateWithLifecycle()
when (uiState) { is UiState.Loading -> CircularProgressIndicator() is UiState.Success -> UserContent(uiState.user) is UiState.Error -> ErrorText(uiState.message) }}Lợi ích:
- ✅ Separation of concerns
- ✅ Testable business logic
- ✅ Survives configuration changes
📚 Tìm hiểu thêm: MVVM trong Android
Q: ViewModel survive configuration change như thế nào?
Phần tiêu đề “Q: ViewModel survive configuration change như thế nào?”Trả lời:
ViewModelStore được retain bởi ViewModelStoreOwner (Activity/Fragment) qua configuration changes.
flowchart TD A["Activity Created"] --> B["ViewModelStore Created"] B --> C["ViewModel Created"] C --> D["Configuration Change"] D --> E["Activity Destroyed"] E --> F["Activity Recreated"] F --> G["Same ViewModelStore"] G --> H["Same ViewModel Instance"]
style D fill:#fff3e0 style H fill:#c8e6c9Internal mechanism:
ViewModelStorelưu trữ ViewModel instances theo key- Khi configuration change,
NonConfigurationInstancesgiữ reference đến ViewModelStore - Activity mới nhận lại cùng ViewModelStore
// ViewModel tồn tại qua rotationclass MainViewModel : ViewModel() { private val _data = MutableStateFlow<List<Item>>(emptyList()) val data = _data.asStateFlow()
init { loadData() // Called only once, not on rotation }
override fun onCleared() { // Called when Activity is truly finishing }}📚 Tìm hiểu thêm: ViewModel
2. MVI (Model-View-Intent)
Phần tiêu đề “2. MVI (Model-View-Intent)”Q: MVI khác gì MVVM?
Phần tiêu đề “Q: MVI khác gì MVVM?”Trả lời:
MVI thêm unidirectional data flow với Intent (user actions):
| Aspect | MVVM | MVI |
|---|---|---|
| Data Flow | Bidirectional | Unidirectional |
| State | Multiple LiveData/Flows | Single immutable state |
| Events | Direct function calls | Intent objects |
| Complexity | Simpler | More structured |
flowchart LR V["View"] -->|"Intent"| VM["ViewModel"] VM -->|"new State"| V VM --> M["Model"] M --> VM
style V fill:#e3f2fd style VM fill:#fff3e0// Intent (User Actions)sealed class UserIntent { data class LoadUser(val id: String) : UserIntent() object Refresh : UserIntent() data class UpdateName(val name: String) : UserIntent()}
// State (Single source of truth)data class UserState( val isLoading: Boolean = false, val user: User? = null, val error: String? = null)
// ViewModel processes intentsclass UserViewModel : ViewModel() {
private val _state = MutableStateFlow(UserState()) val state = _state.asStateFlow()
fun processIntent(intent: UserIntent) { when (intent) { is UserIntent.LoadUser -> loadUser(intent.id) is UserIntent.Refresh -> refresh() is UserIntent.UpdateName -> updateName(intent.name) } }
private fun loadUser(id: String) { viewModelScope.launch { _state.update { it.copy(isLoading = true) } try { val user = repository.getUser(id) _state.update { it.copy(isLoading = false, user = user) } } catch (e: Exception) { _state.update { it.copy(isLoading = false, error = e.message) } } } }}
// View sends intents@Composablefun UserScreen(viewModel: UserViewModel) { val state by viewModel.state.collectAsStateWithLifecycle()
LaunchedEffect(Unit) { viewModel.processIntent(UserIntent.LoadUser("123")) }
Button(onClick = { viewModel.processIntent(UserIntent.Refresh) }) { Text("Refresh") }}Khi nào dùng MVI:
- ✅ Complex state với nhiều interactions
- ✅ Need time-travel debugging
- ✅ Team prefers strict patterns
3. Clean Architecture
Phần tiêu đề “3. Clean Architecture”Q: Clean Architecture trong Android?
Phần tiêu đề “Q: Clean Architecture trong Android?”Trả lời:
Clean Architecture chia app thành layers với dependency rule: outer layers depend on inner layers.
flowchart TB subgraph Presentation["Presentation Layer"] UI["UI<br/>(Compose/XML)"] VM["ViewModel"] end
subgraph Domain["Domain Layer"] UC["Use Cases"] RE["Repository<br/>Interfaces"] EN["Entities"] end
subgraph Data["Data Layer"] RI["Repository<br/>Implementation"] DS["Data Sources<br/>(API, DB)"] end
UI --> VM VM --> UC UC --> RE RI -.-> RE RI --> DS
style Domain fill:#e8f5e9| Layer | Contents | Dependencies |
|---|---|---|
| Domain | Use Cases, Entities, Repository interfaces | None (pure Kotlin) |
| Data | Repository impl, Data Sources, Mappers | Domain |
| Presentation | UI, ViewModels | Domain |
// Domain Layer - Pure Kotlin, no Android dependenciesdata class User(val id: String, val name: String)
interface UserRepository { suspend fun getUser(id: String): User suspend fun saveUser(user: User)}
class GetUserUseCase(private val repository: UserRepository) { suspend operator fun invoke(id: String): Result<User> { return runCatching { repository.getUser(id) } }}
// Data Layerclass UserRepositoryImpl( private val api: UserApi, private val dao: UserDao) : UserRepository {
override suspend fun getUser(id: String): User { return try { api.fetchUser(id).also { dao.insert(it.toEntity()) } } catch (e: Exception) { dao.getById(id)?.toUser() ?: throw e } }}
// Presentation Layerclass UserViewModel( private val getUserUseCase: GetUserUseCase) : ViewModel() {
fun loadUser(id: String) { viewModelScope.launch { getUserUseCase(id) .onSuccess { _user.value = it } .onFailure { _error.value = it.message } } }}📚 Tìm hiểu thêm: Clean Architecture
4. Dependency Injection
Phần tiêu đề “4. Dependency Injection”Q: Dependency Injection là gì? Hilt vs Koin?
Phần tiêu đề “Q: Dependency Injection là gì? Hilt vs Koin?”Trả lời:
DI là pattern cung cấp dependencies từ bên ngoài thay vì tạo trong class.
// ❌ Without DI - tight couplingclass UserViewModel { private val repository = UserRepository( UserApi(RetrofitClient.create()), UserDatabase.getInstance(context).userDao() )}
// ✅ With DI - loose couplingclass UserViewModel( private val repository: UserRepository // Injected) : ViewModel()Hilt vs Koin:
| Aspect | Hilt | Koin |
|---|---|---|
| Type | Compile-time | Runtime |
| Error Detection | Compile time | Runtime |
| Performance | Faster (no reflection) | Slower (uses reflection) |
| Setup | More boilerplate | Simpler |
| Google Support | ✅ Official | Community |
Hilt Example:
// Module@Module@InstallIn(SingletonComponent::class)object AppModule {
@Provides @Singleton fun provideUserRepository( api: UserApi, dao: UserDao ): UserRepository = UserRepositoryImpl(api, dao)}
// ViewModel@HiltViewModelclass UserViewModel @Inject constructor( private val repository: UserRepository) : ViewModel()
// Activity@AndroidEntryPointclass MainActivity : ComponentActivity()Koin Example:
// Moduleval appModule = module { single<UserRepository> { UserRepositoryImpl(get(), get()) } viewModel { UserViewModel(get()) }}
// Applicationclass MyApp : Application() { override fun onCreate() { super.onCreate() startKoin { modules(appModule) } }}
// Usageclass UserViewModel( private val repository: UserRepository) : ViewModel()📚 Tìm hiểu thêm: Dependency Injection, Koin
5. Repository Pattern
Phần tiêu đề “5. Repository Pattern”Q: Repository pattern là gì?
Phần tiêu đề “Q: Repository pattern là gì?”Trả lời:
Repository là abstraction layer giữa data sources và business logic:
flowchart LR VM["ViewModel"] --> R["Repository"] R --> A["Remote<br/>(API)"] R --> L["Local<br/>(Room)"] R --> C["Cache<br/>(Memory)"]
style R fill:#fff3e0Responsibilities:
- Abstract data sources
- Implement caching strategy
- Handle offline-first logic
- Map DTOs to domain entities
class UserRepository( private val api: UserApi, private val dao: UserDao, private val cache: UserCache) { // Single source of truth pattern fun getUsers(): Flow<List<User>> = flow { // 1. Emit cached data first cache.getUsers()?.let { emit(it) }
// 2. Fetch from network try { val users = api.fetchUsers() dao.insertAll(users.map { it.toEntity() }) cache.setUsers(users) emit(users) } catch (e: Exception) { // 3. Fallback to local DB emit(dao.getAll().map { it.toUser() }) } }
// Offline-first with NetworkBoundResource fun getUser(id: String): Flow<Resource<User>> = networkBoundResource( query = { dao.getById(id).map { it?.toUser() } }, fetch = { api.fetchUser(id) }, saveFetchResult = { dao.insert(it.toEntity()) }, shouldFetch = { it == null || cache.isExpired(id) } )}6. Design Principles
Phần tiêu đề “6. Design Principles”Q: SOLID principles trong Android?
Phần tiêu đề “Q: SOLID principles trong Android?”Trả lời:
| Principle | Description | Android Example |
|---|---|---|
| Single Responsibility | One class, one job | Separate ViewModel, Repository |
| Open/Closed | Open for extension, closed for modification | Use interfaces |
| Liskov Substitution | Subtypes replaceable | Repository implementations |
| Interface Segregation | Small, focused interfaces | Separate Dao interfaces |
| Dependency Inversion | Depend on abstractions | Inject interfaces, not implementations |
// Single Responsibilityclass UserRepository { /* only data operations */ }class UserValidator { /* only validation */ }class UserMapper { /* only mapping */ }
// Open/Closed - extend via interfacesinterface DataSource<T> { suspend fun get(id: String): T? suspend fun save(item: T)}
class ApiDataSource : DataSource<UserDto> { /* ... */ }class LocalDataSource : DataSource<UserEntity> { /* ... */ }
// Dependency Inversionclass UserViewModel( private val repository: UserRepository // Interface, not UserRepositoryImpl) : ViewModel()Q: Separation of Concerns trong Android?
Phần tiêu đề “Q: Separation of Concerns trong Android?”Trả lời:
Mỗi component chỉ làm một việc:
| Component | Concern | Should NOT |
|---|---|---|
| Activity/Fragment | Host UI, handle navigation | Business logic, data fetching |
| Composable | Render UI | Direct API calls |
| ViewModel | UI state, orchestrate | Database access directly |
| Repository | Data operations | UI updates |
| Use Case | Business rules | Data persistence |
// ❌ Bad - Activity does everythingclass BadActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { val users = Retrofit.create(UserApi::class.java) .fetchUsers() // Network in Activity!
Room.databaseBuilder(this, AppDb::class.java, "db") .build().userDao().insertAll(users) // DB in Activity! }}
// ✅ Good - Separation of concerns@Composablefun UserScreen(viewModel: UserViewModel = hiltViewModel()) { val users by viewModel.users.collectAsStateWithLifecycle()
LazyColumn { items(users) { UserItem(it) } }}
class UserViewModel(private val repository: UserRepository) : ViewModel() { val users = repository.getUsers().stateIn( viewModelScope, SharingStarted.WhileSubscribed(), emptyList() )}📝 Architecture Decision Guide
Phần tiêu đề “📝 Architecture Decision Guide”| Scenario | Recommended |
|---|---|
| Small app, simple state | MVVM |
| Complex state, many interactions | MVI |
| Large team, maintainability | Clean Architecture |
| Quick prototype | MVVM + Koin |
| Production app, Google stack | MVVM + Hilt |
| Multiple data sources | Repository pattern |
📚 Tìm hiểu thêm: MVVM, Clean Architecture