androidandroid-jetpack-composeclean-architectureandroid-paging-3

Clean Architecture with paging3 and compose passing PagingData to domain layer


I am using clean architecture with paging 3 and trying to populate a lazyColumn.

I have the following class in my data layer module and I don't want to pass the PagingData to the domain layer as I want to keep the domain free of any Android SDK.

class RepositoryImp @Inject constructor(
    private val foodService: FoodService,
    private val foodDatabase: FoodDatabase) : Repository {

    @OptIn(ExperimentalPagingApi::class)
    override fun fetchAllComplexSearch(): Flow<ResponseState<List<ComplexSearchEntity>>> {
        val pagingSourceFactory = { foodDatabase.foodDao().fetchAllComplexSearchPaging() }

        val pagingDataResult = Pager(
            config = PagingConfig(pageSize = ITEMS_PER_PAGE_DEFAULT),
            remoteMediator = ComplexSearchRemoteMediator(
                foodDatabase = foodDatabase, foodService = foodService
            ),
            pagingSourceFactory = pagingSourceFactory
        ).flow


        val data = pagingDataResult.map { pagingData ->
            pagingData.map { complexSearchModel ->
                ResponseState.Success(
                    listOf(
                        ComplexSearchEntity(
                            complexSearchModel.id,
                            complexSearchModel.title,
                            complexSearchModel.image,
                            complexSearchModel.imageType
                        )
                    )
                )
            }
        }
        
        return data
    }

I want to return this Flow<ResponseState<List<ComplexSearchEntity>>> But I get the following error:

Type mismatch.
Required:
Flow<ResponseState<List<ComplexSearchEntity>>>
Found:
Flow<PagingData<ResponseState.Success<List<ComplexSearchEntity>>>>

It seems like I am wrapping the ResponseState.Success(...) inside the PagingData

Then the presentation layer module will map the Response.Success in a PagingData to be used as a LazyPagingItems<ComplexSearchEntity> in a LazyColumn.


Solution

  • To achieve the desired functionality, your repository should expose a PagingData<ComplexSearchEntity> rather than a ResponseState<List<ComplexSearchEntity>>. This adjustment allows for a cleaner architecture by keeping paging logic within the repository layer and not spreading data layer objects to other layers, thus maintaining a clear separation of concerns.

    For your domain model to support this, you can include the following dependency, which is pure Kotlin compatible and allows a repository or use case to expose PagingData:

    implementation("androidx.paging:paging-common:3.2.1")
    

    Then, within your repository implementation, you should move the logic for creating the Pager from the UI or ViewModel layer to the repository itself. Here's how you could implement this:

    @OptIn(ExperimentalPagingApi::class)
    override fun fetchAllComplexSearch(): Flow<PagingData<ComplexSearchEntity>> {
        val pagingSourceFactory = { foodDatabase.foodDao().fetchAllComplexSearchPaging() }
    
        return Pager(
            config = PagingConfig(pageSize = ITEMS_PER_PAGE_DEFAULT),
            remoteMediator = ComplexSearchRemoteMediator(
                foodDatabase = foodDatabase, foodService = foodService
            ),
            pagingSourceFactory = pagingSourceFactory
        ).flow
    }