106. Clean Architecture在Android大型项目中的实战应用
Repository接口定义在Domain层,实现在Data层。package com.某品牌.domain.repository/*** 设备仓储接口* 定义数据访问契约,不关心具体实现*//*** 获取设备详情*//*** 获取用户的所有设备*//*** 添加设备*//*** 更新设备信息*//*** 删除设备*//*** 观察设备状态变化*//*** 观察设备列表变化*//*** 同步设备数
106. Clean Architecture在Android大型项目中的实战应用
摘要
本文深入探讨Clean Architecture在Android大型项目中的实战应用。通过分析智能家居IoT平台的架构演进,详细阐述Clean Architecture的分层设计、依赖规则、用例设计等核心概念,并提供完整的Kotlin实现方案。文章涵盖从理论到实践的完整路径,包括架构分层、边界定义、数据流转、测试策略等关键环节,为大型项目的架构设计提供可落地的参考方案。
关键词:Clean Architecture、分层架构、依赖倒置、用例设计、领域驱动设计、架构边界
一、Clean Architecture核心理念
1.1 架构目标
Clean Architecture的核心目标是构建可维护、可测试、独立于框架的系统。
1.2 分层架构
Clean Architecture采用同心圆分层模型,内层不依赖外层。
二、架构分层设计
2.1 Domain层(核心业务层)
Domain层包含企业业务规则和实体,完全独立于Android框架。
实体定义:
// domain/model/Device.kt
package com.某品牌.domain.model
/**
* 设备领域模型
* 包含核心业务规则,与框架无关
*/
data class Device(
val id: DeviceId,
val name: String,
val type: DeviceType,
val status: DeviceStatus,
val capabilities: List<Capability>,
val metadata: DeviceMetadata
) {
/**
* 业务规则:检查设备是否可控制
*/
fun isControllable(): Boolean {
return status == DeviceStatus.ONLINE &&
capabilities.isNotEmpty()
}
/**
* 业务规则:执行能力验证
*/
fun canExecute(capability: Capability): Boolean {
return capabilities.contains(capability) &&
isControllable()
}
/**
* 业务规则:检查是否需要固件升级
*/
fun needsFirmwareUpdate(latestVersion: String): Boolean {
return metadata.firmwareVersion < latestVersion
}
}
/**
* 值对象:设备ID
*/
@JvmInline
value class DeviceId(val value: String) {
init {
require(value.isNotBlank()) { "Device ID cannot be blank" }
}
}
/**
* 枚举:设备类型
*/
enum class DeviceType {
CAMERA,
DOORBELL,
LOCK,
SENSOR,
LIGHT
}
/**
* 枚举:设备状态
*/
enum class DeviceStatus {
ONLINE,
OFFLINE,
UPGRADING,
ERROR
}
/**
* 设备能力
*/
sealed class Capability {
data class PowerControl(val maxPower: Int) : Capability()
data class BrightnessControl(val range: IntRange) : Capability()
data class MotionDetection(val sensitivity: Float) : Capability()
data class TwoWayAudio(val sampleRate: Int) : Capability()
}
/**
* 设备元数据
*/
data class DeviceMetadata(
val firmwareVersion: String,
val hardwareVersion: String,
val manufacturer: String,
val model: String,
val serialNumber: String,
val createdAt: Long,
val updatedAt: Long
)
领域服务:
// domain/service/DeviceAuthorizationService.kt
package com.某品牌.domain.service
/**
* 设备授权领域服务
* 处理复杂的设备授权业务逻辑
*/
class DeviceAuthorizationService {
/**
* 检查用户是否有权限控制设备
*/
fun canUserControlDevice(
user: User,
device: Device,
operation: DeviceOperation
): AuthorizationResult {
// 业务规则1:设备必须在线且可控制
if (!device.isControllable()) {
return AuthorizationResult.Failure(
reason = "Device is not controllable"
)
}
// 业务规则2:用户必须是设备所有者或被授权用户
if (!user.hasAccessTo(device.id)) {
return AuthorizationResult.Failure(
reason = "User not authorized"
)
}
// 业务规则3:检查操作权限级别
val requiredLevel = operation.requiredPermissionLevel()
val userLevel = user.getPermissionLevel(device.id)
if (userLevel < requiredLevel) {
return AuthorizationResult.Failure(
reason = "Insufficient permission level"
)
}
return AuthorizationResult.Success
}
}
/**
* 授权结果
*/
sealed class AuthorizationResult {
object Success : AuthorizationResult()
data class Failure(val reason: String) : AuthorizationResult()
}
2.2 Use Case层(应用业务层)
Use Case层定义应用特定的业务规则。
// domain/usecase/GetDeviceListUseCase.kt
package com.某品牌.domain.usecase
/**
* 获取设备列表用例
* 编排业务流程,协调多个repository和service
*/
class GetDeviceListUseCase(
private val deviceRepository: DeviceRepository,
private val userRepository: UserRepository,
private val authorizationService: DeviceAuthorizationService
) {
/**
* 执行用例
* @param filter 过滤条件
* @return 设备列表结果
*/
suspend operator fun invoke(
filter: DeviceFilter
): Result<List<Device>> = runCatching {
// 1. 获取当前用户
val currentUser = userRepository.getCurrentUser()
?: throw IllegalStateException("User not logged in")
// 2. 获取用户有权限的设备列表
val allDevices = deviceRepository.getDevicesByUser(currentUser.id)
// 3. 应用过滤条件
val filteredDevices = allDevices.filter { device ->
applyFilter(device, filter)
}
// 4. 按状态和类型排序
val sortedDevices = filteredDevices.sortedWith(
compareByDescending<Device> { it.status == DeviceStatus.ONLINE }
.thenBy { it.type }
.thenBy { it.name }
)
sortedDevices
}
private fun applyFilter(device: Device, filter: DeviceFilter): Boolean {
if (filter.types.isNotEmpty() && device.type !in filter.types) {
return false
}
if (filter.statuses.isNotEmpty() && device.status !in filter.statuses) {
return false
}
if (filter.searchQuery.isNotBlank()) {
val query = filter.searchQuery.lowercase()
if (!device.name.lowercase().contains(query) &&
!device.metadata.model.lowercase().contains(query)) {
return false
}
}
return true
}
}
/**
* 设备过滤条件
*/
data class DeviceFilter(
val types: Set<DeviceType> = emptySet(),
val statuses: Set<DeviceStatus> = emptySet(),
val searchQuery: String = ""
)
复杂用例示例:
// domain/usecase/ControlDeviceUseCase.kt
package com.某品牌.domain.usecase
/**
* 控制设备用例
* 包含完整的业务流程:授权、验证、执行、记录
*/
class ControlDeviceUseCase(
private val deviceRepository: DeviceRepository,
private val userRepository: UserRepository,
private val authorizationService: DeviceAuthorizationService,
private val commandExecutor: DeviceCommandExecutor,
private val eventRepository: DeviceEventRepository
) {
suspend operator fun invoke(
deviceId: DeviceId,
command: DeviceCommand
): Result<CommandExecutionResult> = runCatching {
// 1. 获取设备信息
val device = deviceRepository.getDevice(deviceId)
?: throw DeviceNotFoundException(deviceId)
// 2. 获取当前用户
val currentUser = userRepository.getCurrentUser()
?: throw UserNotAuthenticatedException()
// 3. 授权检查
val authResult = authorizationService.canUserControlDevice(
user = currentUser,
device = device,
operation = command.toOperation()
)
if (authResult is AuthorizationResult.Failure) {
throw UnauthorizedException(authResult.reason)
}
// 4. 验证命令参数
validateCommand(device, command)
// 5. 执行命令
val result = commandExecutor.execute(device, command)
// 6. 记录事件
eventRepository.recordEvent(
DeviceEvent(
deviceId = device.id,
userId = currentUser.id,
type = EventType.COMMAND_EXECUTED,
command = command,
result = result,
timestamp = System.currentTimeMillis()
)
)
result
}
private fun validateCommand(device: Device, command: DeviceCommand) {
// 验证设备是否支持该命令
val capability = command.requiredCapability()
if (!device.canExecute(capability)) {
throw UnsupportedCommandException(
"Device does not support $capability"
)
}
// 验证命令参数范围
command.validate()
}
}
/**
* 设备命令
*/
sealed class DeviceCommand {
abstract fun requiredCapability(): Capability
abstract fun validate()
abstract fun toOperation(): DeviceOperation
data class SetPower(val on: Boolean) : DeviceCommand() {
override fun requiredCapability() = Capability.PowerControl(0)
override fun validate() {}
override fun toOperation() = DeviceOperation.POWER_CONTROL
}
data class SetBrightness(val level: Int) : DeviceCommand() {
override fun requiredCapability() =
Capability.BrightnessControl(0..100)
override fun validate() {
require(level in 0..100) {
"Brightness level must be between 0 and 100"
}
}
override fun toOperation() = DeviceOperation.BRIGHTNESS_CONTROL
}
data class StartRecording(val duration: Long) : DeviceCommand() {
override fun requiredCapability() =
Capability.MotionDetection(0.5f)
override fun validate() {
require(duration > 0) {
"Recording duration must be positive"
}
}
override fun toOperation() = DeviceOperation.RECORDING_CONTROL
}
}
/**
* 命令执行结果
*/
sealed class CommandExecutionResult {
data class Success(val message: String) : CommandExecutionResult()
data class Failure(val error: String, val code: Int) : CommandExecutionResult()
}
2.3 Repository接口定义
Repository接口定义在Domain层,实现在Data层。
// domain/repository/DeviceRepository.kt
package com.某品牌.domain.repository
/**
* 设备仓储接口
* 定义数据访问契约,不关心具体实现
*/
interface DeviceRepository {
/**
* 获取设备详情
*/
suspend fun getDevice(id: DeviceId): Device?
/**
* 获取用户的所有设备
*/
suspend fun getDevicesByUser(userId: UserId): List<Device>
/**
* 添加设备
*/
suspend fun addDevice(device: Device): Result<Device>
/**
* 更新设备信息
*/
suspend fun updateDevice(device: Device): Result<Unit>
/**
* 删除设备
*/
suspend fun deleteDevice(id: DeviceId): Result<Unit>
/**
* 观察设备状态变化
*/
fun observeDevice(id: DeviceId): Flow<Device>
/**
* 观察设备列表变化
*/
fun observeDeviceList(userId: UserId): Flow<List<Device>>
/**
* 同步设备数据
*/
suspend fun syncDevices(): Result<Unit>
}
三、数据流与依赖管理
3.1 数据流架构
3.2 依赖注入配置
使用Hilt进行依赖注入:
// di/DomainModule.kt
package com.某品牌.di
@Module
@InstallIn(SingletonComponent::class)
object DomainModule {
@Provides
@Singleton
fun provideDeviceAuthorizationService(): DeviceAuthorizationService {
return DeviceAuthorizationService()
}
@Provides
fun provideGetDeviceListUseCase(
deviceRepository: DeviceRepository,
userRepository: UserRepository,
authorizationService: DeviceAuthorizationService
): GetDeviceListUseCase {
return GetDeviceListUseCase(
deviceRepository,
userRepository,
authorizationService
)
}
@Provides
fun provideControlDeviceUseCase(
deviceRepository: DeviceRepository,
userRepository: UserRepository,
authorizationService: DeviceAuthorizationService,
commandExecutor: DeviceCommandExecutor,
eventRepository: DeviceEventRepository
): ControlDeviceUseCase {
return ControlDeviceUseCase(
deviceRepository,
userRepository,
authorizationService,
commandExecutor,
eventRepository
)
}
}
3.3 Data层实现
// data/repository/DeviceRepositoryImpl.kt
package com.某品牌.data.repository
class DeviceRepositoryImpl(
private val remoteDataSource: DeviceRemoteDataSource,
private val localDataSource: DeviceLocalDataSource,
private val deviceMapper: DeviceMapper
) : DeviceRepository {
override suspend fun getDevice(id: DeviceId): Device? {
// 先从本地获取
val localDevice = localDataSource.getDevice(id.value)
if (localDevice != null) {
return deviceMapper.toDomain(localDevice)
}
// 本地没有则从远程获取
return remoteDataSource.getDevice(id.value)
.map { dto ->
// 保存到本地
val entity = deviceMapper.toEntity(dto)
localDataSource.insertDevice(entity)
deviceMapper.toDomain(entity)
}
.getOrNull()
}
override suspend fun getDevicesByUser(userId: UserId): List<Device> {
return withContext(Dispatchers.IO) {
// 并行获取本地和远程数据
val localDevices = async {
localDataSource.getDevicesByUser(userId.value)
}
val remoteDevices = async {
remoteDataSource.getDevicesByUser(userId.value)
.getOrNull() ?: emptyList()
}
val local = localDevices.await()
val remote = remoteDevices.await()
// 合并并更新本地数据
mergeAndUpdateLocal(local, remote)
}
}
override fun observeDevice(id: DeviceId): Flow<Device> {
return localDataSource.observeDevice(id.value)
.map { deviceMapper.toDomain(it) }
}
override fun observeDeviceList(userId: UserId): Flow<List<Device>> {
return localDataSource.observeDevicesByUser(userId.value)
.map { entities ->
entities.map { deviceMapper.toDomain(it) }
}
}
override suspend fun syncDevices(): Result<Unit> = runCatching {
val userId = getCurrentUserId()
// 1. 获取远程最新数据
val remoteDevices = remoteDataSource.getDevicesByUser(userId)
.getOrThrow()
// 2. 转换并保存到本地
val entities = remoteDevices.map { deviceMapper.toEntity(it) }
localDataSource.replaceAll(entities)
// 3. 清理过期数据
localDataSource.deleteOldDevices(
threshold = System.currentTimeMillis() - 30.days()
)
}
private suspend fun mergeAndUpdateLocal(
local: List<DeviceEntity>,
remote: List<DeviceDto>
): List<Device> {
val localMap = local.associateBy { it.id }
val remoteMap = remote.associateBy { it.id }
// 合并逻辑:远程数据优先
val mergedEntities = remoteMap.values.map { dto ->
deviceMapper.toEntity(dto)
}
// 保存合并后的数据
localDataSource.insertDevices(mergedEntities)
return mergedEntities.map { deviceMapper.toDomain(it) }
}
}
四、Presentation层实现
4.1 ViewModel实现
// presentation/device/DeviceListViewModel.kt
package com.某品牌.presentation.device
class DeviceListViewModel(
private val getDeviceListUseCase: GetDeviceListUseCase,
private val observeDeviceListUseCase: ObserveDeviceListUseCase,
private val controlDeviceUseCase: ControlDeviceUseCase
) : ViewModel() {
// UI状态
private val _uiState = MutableStateFlow<DeviceListUiState>(
DeviceListUiState.Loading
)
val uiState: StateFlow<DeviceListUiState> = _uiState.asStateFlow()
// 当前过滤条件
private val _filter = MutableStateFlow(DeviceFilter())
init {
observeDevices()
}
/**
* 观察设备列表变化
*/
private fun observeDevices() {
viewModelScope.launch {
combine(
observeDeviceListUseCase(),
_filter
) { devices, filter ->
devices to filter
}.collect { (devices, filter) ->
handleDeviceListUpdate(devices, filter)
}
}
}
/**
* 处理设备列表更新
*/
private fun handleDeviceListUpdate(
devices: List<Device>,
filter: DeviceFilter
) {
if (devices.isEmpty()) {
_uiState.value = DeviceListUiState.Empty
return
}
val deviceItems = devices.map { device ->
DeviceUiModel(
id = device.id.value,
name = device.name,
type = device.type.toDisplayString(),
status = device.status.toDisplayStatus(),
isControllable = device.isControllable(),
capabilities = device.capabilities.map { it.toDisplayString() }
)
}
_uiState.value = DeviceListUiState.Success(
devices = deviceItems,
filter = filter
)
}
/**
* 刷新设备列表
*/
fun refreshDevices() {
viewModelScope.launch {
_uiState.value = DeviceListUiState.Loading
getDeviceListUseCase(_filter.value)
.onSuccess { devices ->
// 数据会通过Flow自动更新
}
.onFailure { error ->
_uiState.value = DeviceListUiState.Error(
message = error.message ?: "Unknown error"
)
}
}
}
/**
* 更新过滤条件
*/
fun updateFilter(filter: DeviceFilter) {
_filter.value = filter
}
/**
* 控制设备
*/
fun controlDevice(deviceId: String, command: DeviceCommand) {
viewModelScope.launch {
controlDeviceUseCase(DeviceId(deviceId), command)
.onSuccess { result ->
when (result) {
is CommandExecutionResult.Success -> {
// 显示成功提示
_uiState.update { currentState ->
if (currentState is DeviceListUiState.Success) {
currentState.copy(
message = "Command executed successfully"
)
} else {
currentState
}
}
}
is CommandExecutionResult.Failure -> {
_uiState.update { currentState ->
if (currentState is DeviceListUiState.Success) {
currentState.copy(
message = "Command failed: ${result.error}"
)
} else {
currentState
}
}
}
}
}
.onFailure { error ->
_uiState.update { currentState ->
if (currentState is DeviceListUiState.Success) {
currentState.copy(
message = "Error: ${error.message}"
)
} else {
currentState
}
}
}
}
}
}
/**
* UI状态
*/
sealed class DeviceListUiState {
object Loading : DeviceListUiState()
object Empty : DeviceListUiState()
data class Success(
val devices: List<DeviceUiModel>,
val filter: DeviceFilter,
val message: String? = null
) : DeviceListUiState()
data class Error(val message: String) : DeviceListUiState()
}
/**
* UI模型
*/
data class DeviceUiModel(
val id: String,
val name: String,
val type: String,
val status: DisplayStatus,
val isControllable: Boolean,
val capabilities: List<String>
)
data class DisplayStatus(
val text: String,
val color: Int
)
4.2 架构边界与通信
五、测试策略
5.1 Domain层单元测试
Domain层完全独立于框架,测试简单直接。
// domain/usecase/GetDeviceListUseCaseTest.kt
class GetDeviceListUseCaseTest {
private lateinit var deviceRepository: DeviceRepository
private lateinit var userRepository: UserRepository
private lateinit var authorizationService: DeviceAuthorizationService
private lateinit var useCase: GetDeviceListUseCase
@Before
fun setup() {
deviceRepository = mockk()
userRepository = mockk()
authorizationService = mockk()
useCase = GetDeviceListUseCase(
deviceRepository,
userRepository,
authorizationService
)
}
@Test
fun `invoke should return filtered and sorted devices`() = runTest {
// Given
val userId = UserId("user123")
val user = createTestUser(userId)
val devices = listOf(
createTestDevice(
id = "1",
name = "Camera 1",
type = DeviceType.CAMERA,
status = DeviceStatus.ONLINE
),
createTestDevice(
id = "2",
name = "Doorbell 1",
type = DeviceType.DOORBELL,
status = DeviceStatus.OFFLINE
),
createTestDevice(
id = "3",
name = "Camera 2",
type = DeviceType.CAMERA,
status = DeviceStatus.ONLINE
)
)
coEvery { userRepository.getCurrentUser() } returns user
coEvery { deviceRepository.getDevicesByUser(userId) } returns devices
val filter = DeviceFilter(
types = setOf(DeviceType.CAMERA)
)
// When
val result = useCase(filter)
// Then
assertTrue(result.isSuccess)
val filteredDevices = result.getOrThrow()
assertEquals(2, filteredDevices.size)
assertTrue(filteredDevices.all { it.type == DeviceType.CAMERA })
// 验证排序:在线设备在前
assertEquals(DeviceStatus.ONLINE, filteredDevices[0].status)
assertEquals(DeviceStatus.ONLINE, filteredDevices[1].status)
}
@Test
fun `invoke should fail when user is not logged in`() = runTest {
// Given
coEvery { userRepository.getCurrentUser() } returns null
// When
val result = useCase(DeviceFilter())
// Then
assertTrue(result.isFailure)
assertTrue(result.exceptionOrNull() is IllegalStateException)
}
}
5.2 Use Case集成测试
// domain/usecase/ControlDeviceUseCaseTest.kt
class ControlDeviceUseCaseTest {
private lateinit var deviceRepository: DeviceRepository
private lateinit var userRepository: UserRepository
private lateinit var authorizationService: DeviceAuthorizationService
private lateinit var commandExecutor: DeviceCommandExecutor
private lateinit var eventRepository: DeviceEventRepository
private lateinit var useCase: ControlDeviceUseCase
@Before
fun setup() {
deviceRepository = mockk()
userRepository = mockk()
authorizationService = mockk(relaxed = true)
commandExecutor = mockk()
eventRepository = mockk(relaxed = true)
useCase = ControlDeviceUseCase(
deviceRepository,
userRepository,
authorizationService,
commandExecutor,
eventRepository
)
}
@Test
fun `should execute command successfully when authorized`() = runTest {
// Given
val deviceId = DeviceId("device123")
val device = createControllableDevice(deviceId)
val user = createTestUser()
val command = DeviceCommand.SetPower(true)
coEvery { deviceRepository.getDevice(deviceId) } returns device
coEvery { userRepository.getCurrentUser() } returns user
every {
authorizationService.canUserControlDevice(user, device, any())
} returns AuthorizationResult.Success
coEvery {
commandExecutor.execute(device, command)
} returns CommandExecutionResult.Success("Power turned on")
// When
val result = useCase(deviceId, command)
// Then
assertTrue(result.isSuccess)
val executionResult = result.getOrThrow()
assertTrue(executionResult is CommandExecutionResult.Success)
// 验证事件记录
coVerify {
eventRepository.recordEvent(
match {
it.deviceId == deviceId &&
it.userId == user.id &&
it.type == EventType.COMMAND_EXECUTED
}
)
}
}
@Test
fun `should fail when user is not authorized`() = runTest {
// Given
val deviceId = DeviceId("device123")
val device = createControllableDevice(deviceId)
val user = createTestUser()
val command = DeviceCommand.SetPower(true)
coEvery { deviceRepository.getDevice(deviceId) } returns device
coEvery { userRepository.getCurrentUser() } returns user
every {
authorizationService.canUserControlDevice(user, device, any())
} returns AuthorizationResult.Failure("User not authorized")
// When
val result = useCase(deviceId, command)
// Then
assertTrue(result.isFailure)
assertTrue(result.exceptionOrNull() is UnauthorizedException)
// 验证命令未执行
coVerify(exactly = 0) {
commandExecutor.execute(any(), any())
}
}
}
六、架构演进经验
6.1 迁移策略
从传统架构迁移到Clean Architecture的步骤:
迁移实践:
// 第一步:提取Repository接口
// 从原有的DeviceManager中提取接口
interface DeviceRepository {
suspend fun getDevices(): List<Device>
}
// 第二步:创建领域模型
// 将原有的DTO转换为领域实体
data class Device(
val id: DeviceId,
val name: String,
val status: DeviceStatus
) {
fun isAvailable() = status == DeviceStatus.ONLINE
}
// 第三步:实现Use Case
// 封装业务逻辑
class GetAvailableDevicesUseCase(
private val repository: DeviceRepository
) {
suspend operator fun invoke(): List<Device> {
return repository.getDevices()
.filter { it.isAvailable() }
}
}
// 第四步:重构ViewModel
// 依赖Use Case而不是直接依赖Repository
class DeviceViewModel(
private val getAvailableDevicesUseCase: GetAvailableDevicesUseCase
) : ViewModel() {
fun loadDevices() {
viewModelScope.launch {
val devices = getAvailableDevicesUseCase()
// Update UI state
}
}
}
6.2 常见陷阱
陷阱1:过度设计
// 错误:为简单CRUD创建Use Case
class GetUserByIdUseCase(
private val repository: UserRepository
) {
suspend operator fun invoke(id: String) = repository.getUserById(id)
}
// 正确:简单查询可以直接使用Repository
class UserViewModel(
private val userRepository: UserRepository
) {
fun loadUser(id: String) {
viewModelScope.launch {
val user = userRepository.getUserById(id)
// ...
}
}
}
陷阱2:依赖方向错误
// 错误:Domain层依赖Data层
// domain/model/Device.kt
data class Device(
val entity: DeviceEntity // 依赖了Data层的Entity
)
// 正确:Data层依赖Domain层
// data/mapper/DeviceMapper.kt
class DeviceMapper {
fun toDomain(entity: DeviceEntity): Device {
return Device(
id = DeviceId(entity.id),
name = entity.name
)
}
}
6.3 性能优化
优化1:Use Case结果缓存
class GetDeviceListUseCase(
private val deviceRepository: DeviceRepository
) {
private var cachedResult: List<Device>? = null
private var lastFetchTime: Long = 0
suspend operator fun invoke(
filter: DeviceFilter,
forceRefresh: Boolean = false
): Result<List<Device>> {
// 缓存5分钟有效
val isCacheValid = System.currentTimeMillis() - lastFetchTime < 5.minutes()
if (!forceRefresh && isCacheValid && cachedResult != null) {
return Result.success(cachedResult!!.filter { applyFilter(it, filter) })
}
return runCatching {
val devices = deviceRepository.getDevicesByUser(getCurrentUserId())
cachedResult = devices
lastFetchTime = System.currentTimeMillis()
devices.filter { applyFilter(it, filter) }
}
}
}
七、总结
7.1 Clean Architecture的优势
- 可测试性:业务逻辑独立,易于单元测试
- 可维护性:关注点分离,修改影响范围小
- 独立性:核心业务不依赖框架和工具
- 灵活性:易于替换外部组件
7.2 实施建议
- 渐进式迁移:从新功能开始应用Clean Architecture
- 团队共识:确保团队理解架构原则
- 合理权衡:避免过度设计,保持务实
- 持续优化:根据实际情况调整架构
7.3 适用场景
Clean Architecture适合:
- 大型复杂项目
- 长期维护的项目
- 业务逻辑复杂的应用
- 需要高测试覆盖率的项目
不太适合:
- 小型简单应用
- 快速原型验证
- 业务逻辑简单的工具类应用
参考资源
- Robert C. Martin - Clean Architecture
- Android Architecture Components
- Kotlin Coroutines Best Practices
- Domain-Driven Design
更多推荐



所有评论(0)