Clean Architecture
问题
如何在 Android 中实现 Clean Architecture?
答案
三层架构
| 层 | 职责 | 依赖 |
|---|---|---|
| Presentation | UI 展示、用户交互 | 依赖 Domain |
| Domain | 业务逻辑(纯 Kotlin) | 不依赖任何层 |
| Data | 数据访问实现 | 依赖 Domain(实现接口) |
Domain Layer
// Domain Model(纯数据,无 Android 依赖)
data class User(
val id: String,
val name: String,
val email: String,
val isActive: Boolean
)
// Repository Interface(Domain 只定义接口)
interface UserRepository {
suspend fun getUser(id: String): User
suspend fun getUsers(): List<User>
fun getUsersFlow(): Flow<List<User>>
}
// UseCase(封装单个业务操作)
class GetActiveUsersUseCase(
private val repository: UserRepository
) {
operator fun invoke(): Flow<List<User>> {
return repository.getUsersFlow()
.map { users -> users.filter { it.isActive } }
}
}
Data Layer
// DTO(网络数据传输对象)
@Serializable
data class UserDto(
val id: String,
val name: String,
val email: String,
@SerialName("is_active")
val isActive: Boolean
)
// Mapper(DTO → Domain Model)
fun UserDto.toDomain() = User(
id = id,
name = name,
email = email,
isActive = isActive
)
// Repository 实现
class UserRepositoryImpl(
private val api: ApiService,
private val dao: UserDao
) : UserRepository {
override suspend fun getUser(id: String): User {
return api.getUser(id).toDomain()
}
override fun getUsersFlow(): Flow<List<User>> {
return dao.getAllUsers().map { entities ->
entities.map { it.toDomain() }
}
}
}
Presentation Layer
class UserListViewModel(
private val getActiveUsers: GetActiveUsersUseCase
) : ViewModel() {
val users: StateFlow<List<User>> = getActiveUsers()
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
}
Domain Layer 何时使用?
Domain Layer 是可选的。当出现以下情况时应该引入:
- 多个 ViewModel 共享相同的业务逻辑
- 业务逻辑复杂需要单独测试
- 项目需要清晰的模块边界
简单的 CRUD 页面直接在 ViewModel 中调用 Repository 即可。
常见面试问题
Q1: UseCase 只有一行代码也要写吗?
答案:
简单的 CRUD 不需要 UseCase,直接在 ViewModel 中调用 Repository。UseCase 适用于:
- 组合多个 Repository 的数据
- 包含业务规则(如过滤、验证、计算)
- 多个 ViewModel 共享同一逻辑
Q2: 为什么 Domain Layer 不能依赖 Android?
答案:
Domain Layer 是纯 Kotlin 模块,不依赖 Android 框架,这样:
- 可测试性:直接用 JUnit 测试,不需要 Android 环境
- 可复用性:可以在 KMP(Kotlin Multiplatform)中共享
- 依赖倒置:Domain 定义 Repository 接口,Data 层实现,符合 SOLID 原则