Clean Architecture with paging3 and compose passing PagingData to domain layer
Asked Answered
R

2

6

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.

Reputed answered 20/7, 2022 at 15:29 Comment(2)
Maybe you could initialize your Pager in the viewmodel?Gayton
I don't want to move setting up the Pager in the viewModel. Because the mediator is in the data layer and the presentation layer should not know about the data layer.Reputed
G
1

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
}

Gamble answered 2/3 at 3:13 Comment(0)
G
2

As of this answer you can use common artifact of paging library in your domain module.

Gayton answered 20/7, 2022 at 16:38 Comment(0)
G
1

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
}

Gamble answered 2/3 at 3:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.