Skip to Content
Android💼 Phỏng vấn AndroidArchitecture & Design Patterns

Câu hỏi phỏng vấn Architecture & Design Patterns

Phần này tổng hợp các câu hỏi về Architecture patternsDesign principles trong Android development.


1. MVVM (Model-View-ViewModel)

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:

LayerResponsibilityAndroid Components
ModelData + business logicRepository, Room, Retrofit
ViewDisplay UI, handle inputActivity, Fragment, Composable
ViewModelHold UI state, transform dataViewModel class
// Model (Repository) class UserRepository(private val api: UserApi) { suspend fun getUser(id: String): User = api.fetchUser(id) } // ViewModel class 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) @Composable fun 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?

Trả lời:

ViewModelStore được retain bởi ViewModelStoreOwner (Activity/Fragment) qua configuration changes.

Internal mechanism:

  1. ViewModelStore lưu trữ ViewModel instances theo key
  2. Khi configuration change, NonConfigurationInstances giữ reference đến ViewModelStore
  3. Activity mới nhận lại cùng ViewModelStore
// ViewModel tồn tại qua rotation class 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)

Q: MVI khác gì MVVM?

Trả lời:

MVI thêm unidirectional data flow với Intent (user actions):

AspectMVVMMVI
Data FlowBidirectionalUnidirectional
StateMultiple LiveData/FlowsSingle immutable state
EventsDirect function callsIntent objects
ComplexitySimplerMore structured
// 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 intents class 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 @Composable fun 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

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.

LayerContentsDependencies
DomainUse Cases, Entities, Repository interfacesNone (pure Kotlin)
DataRepository impl, Data Sources, MappersDomain
PresentationUI, ViewModelsDomain
// Domain Layer - Pure Kotlin, no Android dependencies data 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 Layer class 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 Layer class 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

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 coupling class UserViewModel { private val repository = UserRepository( UserApi(RetrofitClient.create()), UserDatabase.getInstance(context).userDao() ) } // ✅ With DI - loose coupling class UserViewModel( private val repository: UserRepository // Injected ) : ViewModel()

Hilt vs Koin:

AspectHiltKoin
TypeCompile-timeRuntime
Error DetectionCompile timeRuntime
PerformanceFaster (no reflection)Slower (uses reflection)
SetupMore boilerplateSimpler
Google Support✅ OfficialCommunity

Hilt Example:

// Module @Module @InstallIn(SingletonComponent::class) object AppModule { @Provides @Singleton fun provideUserRepository( api: UserApi, dao: UserDao ): UserRepository = UserRepositoryImpl(api, dao) } // ViewModel @HiltViewModel class UserViewModel @Inject constructor( private val repository: UserRepository ) : ViewModel() // Activity @AndroidEntryPoint class MainActivity : ComponentActivity()

Koin Example:

// Module val appModule = module { single<UserRepository> { UserRepositoryImpl(get(), get()) } viewModel { UserViewModel(get()) } } // Application class MyApp : Application() { override fun onCreate() { super.onCreate() startKoin { modules(appModule) } } } // Usage class UserViewModel( private val repository: UserRepository ) : ViewModel()

📚 Tìm hiểu thêm: Dependency Injection, Koin


5. Repository Pattern

Q: Repository pattern là gì?

Trả lời:

Repository là abstraction layer giữa data sources và business logic:

Responsibilities:

  • 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

Q: SOLID principles trong Android?

Trả lời:

PrincipleDescriptionAndroid Example
Single ResponsibilityOne class, one jobSeparate ViewModel, Repository
Open/ClosedOpen for extension, closed for modificationUse interfaces
Liskov SubstitutionSubtypes replaceableRepository implementations
Interface SegregationSmall, focused interfacesSeparate Dao interfaces
Dependency InversionDepend on abstractionsInject interfaces, not implementations
// Single Responsibility class UserRepository { /* only data operations */ } class UserValidator { /* only validation */ } class UserMapper { /* only mapping */ } // Open/Closed - extend via interfaces interface DataSource<T> { suspend fun get(id: String): T? suspend fun save(item: T) } class ApiDataSource : DataSource<UserDto> { /* ... */ } class LocalDataSource : DataSource<UserEntity> { /* ... */ } // Dependency Inversion class UserViewModel( private val repository: UserRepository // Interface, not UserRepositoryImpl ) : ViewModel()

Q: Separation of Concerns trong Android?

Trả lời:

Mỗi component chỉ làm một việc:

ComponentConcernShould NOT
Activity/FragmentHost UI, handle navigationBusiness logic, data fetching
ComposableRender UIDirect API calls
ViewModelUI state, orchestrateDatabase access directly
RepositoryData operationsUI updates
Use CaseBusiness rulesData persistence
// ❌ Bad - Activity does everything class 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 @Composable fun 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

ScenarioRecommended
Small app, simple stateMVVM
Complex state, many interactionsMVI
Large team, maintainabilityClean Architecture
Quick prototypeMVVM + Koin
Production app, Google stackMVVM + Hilt
Multiple data sourcesRepository pattern

📚 Tìm hiểu thêm: MVVM, Clean Architecture

Last updated on