多模块项目分离数据与领域层并遵循SOLID依赖倒置的问题
解决方案
1. 明确模块职责与依赖边界
先梳理三个核心模块的职责和依赖关系,从根源消除冗余依赖:
- domain模块:仅存放业务逻辑的抽象定义(接口、实体类、业务规则),不依赖任何Android框架或数据层具体库,是整个项目的核心契约层
- data模块:实现domain层的抽象接口,负责数据获取、缓存、分页等底层细节,仅依赖domain模块和必要的第三方库(如Jetpack Paging、Retrofit、Room)
- feature模块:仅依赖domain模块,通过domain的抽象接口调用业务逻辑,完全不需要感知data层的存在
2. 重构分页逻辑,修复依赖倒置问题
2.1 在domain层定义分页仓库的抽象接口
把原来的BasePagingRepository抽象类改成domain层的接口,只暴露业务层面的分页能力,避免耦合数据层的实现细节:
// domain模块 - PagingRepository.kt interface PagingRepository<T> { fun getPagedData(): Flow<PagingData<T>> } // 针对TVShow业务的专属仓库接口 interface TVShowPagingRepository : PagingRepository<TVShow>
2.2 在data层实现分页细节
原来的BasePagingSource属于数据层的实现细节,放在data模块中。具体仓库类实现domain层的抽象接口,内部封装分页逻辑:
// data模块 - TVShowPagingRepositoryImpl.kt class TVShowPagingRepositoryImpl( private val api: TVShowApi, private val db: TVShowDatabase ) : TVShowPagingRepository { override fun getPagedData(): Flow<PagingData<TVShow>> { return Pager( config = PagingConfig(pageSize = 20), pagingSourceFactory = { TVShowPagingSource(api, db) } ).flow } } // data模块 - TVShowPagingSource.kt(继承BasePagingSource) class TVShowPagingSource( private val api: TVShowApi, private val db: TVShowDatabase ) : BasePagingSource<Int, TVShow>() { // 实现load、refresh等具体分页逻辑 }
2.3 调整ViewModel的依赖注入
ViewModel现在只依赖domain层的抽象接口,而非具体实现,完全符合依赖倒置原则:
// feature模块 - TVShowViewModel.kt class TVShowViewModel( private val repository: TVShowPagingRepository // 依赖抽象而非具体类 ) : ViewModel() { val pagedTVShows = repository.getPagedData().cachedIn(viewModelScope) }
通过依赖注入框架(如Hilt)在data层完成抽象与实现的绑定:
// data模块 - DataModule.kt @Module @InstallIn(SingletonComponent::class) object DataModule { @Provides fun provideTVShowPagingRepository( api: TVShowApi, db: TVShowDatabase ): TVShowPagingRepository { return TVShowPagingRepositoryImpl(api, db) } }
3. 消除Feature模块的冗余依赖
调整各模块的构建配置,严格控制依赖范围:
- Feature模块仅依赖domain模块:
// feature模块 build.gradle.kts dependencies { implementation(project(":domain")) // 仅添加AndroidX、ViewModel、Paging通用API等必要依赖,不引入:data模块 }
- Data模块仅依赖domain模块和数据层工具库:
// data模块 build.gradle.kts dependencies { implementation(project(":domain")) implementation("androidx.paging:paging-runtime:3.2.1") // 添加Retrofit、Room等数据层依赖 }
- Domain模块保持纯净,仅依赖通用Kotlin库:
// domain模块 build.gradle.kts dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") implementation("androidx.paging:paging-common:3.2.1") // 仅引入Paging的通用API,而非runtime }
额外优化:复用通用分页逻辑
如果多个业务模块都需要分页,可以在data层抽离通用分页实现,避免重复代码:
// data模块 - BasePagingRepositoryImpl.kt abstract class BasePagingRepositoryImpl<T> : PagingRepository<T> { abstract fun createPagingSource(): PagingSource<Int, T> override fun getPagedData(): Flow<PagingData<T>> { return Pager( config = PagingConfig(pageSize = 20), pagingSourceFactory = { createPagingSource() } ).flow } } // TVShow仓库直接继承通用实现 class TVShowPagingRepositoryImpl( private val api: TVShowApi, private val db: TVShowDatabase ) : BasePagingRepositoryImpl<TVShow>(), TVShowPagingRepository { override fun createPagingSource(): PagingSource<Int, TVShow> { return TVShowPagingSource(api, db) } }
内容的提问来源于stack exchange,提问作者Ali
相关产品推荐
相关产品推荐

