You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多模块项目分离数据与领域层并遵循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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 00:55:05