Android - DataSource vs Repository
Asked Answered
P

3

14

You hear a lot of people talking about repositories and datasources when talking about design patterns like MVVM or MVI. I've been creating repositories for things like Retrofit and API calls, and datasources for things like Firebase or other libraries. But I'm not 100% sure I understand the difference between a datasource and a repository in the Android world.

Can someone enlighten me please?

Thanks in advance.

Plumy answered 9/3, 2021 at 22:30 Comment(0)
B
16

As pointed out in the previous answer, going through the guide to app architecture can help understand the relation between data sources and the repository.

Here's a useful quote from the mentioned guide to clarify the relation:

Repository modules handle data operations. They provide a clean API so that the rest of the app can retrieve this data easily. They know where to get the data from and what API calls to make when data is updated. You can consider repositories to be mediators between different data sources, such as persistent models, web services, and caches.

The things you've mentioned in your question (retrofit, firebase, etc.) are all different data sources. You can remove the repository from the equation and handle all data operations individually. But the repository module serves as an abstraction layer between different data sources and the rest of your app.

Billibilliard answered 10/3, 2021 at 8:33 Comment(3)
Thank you very much, this clears things up, I previously thought data sources and repositories were at the same level, when in fact, repositories are between viewmodels and datasources. Thanks again.Plumy
While we're here, if we follow my example above, should both the FirebaseDataSource and RetrofitApi interface be injected into the repository? Or I should inject only one data source class into the repo with the retrofit interface injected into the data source? So Repository(retrofitApi, firebaseDataSource) or Repository(RemoteDataSource(retrofitApi, firebase))Plumy
That sounds like a matter of taste or at least it depends on your use case. There's no problem if you want to add an additional mediator for managing your remote data sources. Although, if you don't have too much logic before passing the data to the repository, you might as well just inject them directly to the repository and handle all the operations there.Billibilliard
L
13

The repository is the API that the viewmodels use to get data. The repository has access to the datasources and decides where to get the data from. See Android's Guide to app architecture.

Android MVVM architecture.

Looselimbed answered 9/3, 2021 at 23:22 Comment(1)
Thank you for this diagram. I can clearly see the difference now!Plumy
P
2

Hope this help people who feels like this is redundant at first, but if you understand it clearly, you'll see it's difference

First we have remote and local data sources.

Remote = Retrofit or Ktor

Local = Room or SqlDelight

class RemoteDataSource (private val userInfoApi: UserInfoApi) {
    suspend fun getUserInfo(): Result<UserInfoDTO> {
        return when (val result = userInfoApi.getUserInfo()) {
            is Result.Error -> {
                Result.Error(result.exception)
            }

            is Result.Success -> {
                Result.Success(result.data)
            }
        }
    }
}

class LocalDataSource (private val userDatabase: UserDataBase) {
    suspend fun getUserInfo(): Result<UserInfoDTO> {
        return when (val result = userDatabase.getUserInfo()) {
            is Result.Error -> {
                Result.Error(result.exception)
            }

            is Result.Success -> {
                Result.Success(result.data)
            }
        }
    }
}

Then this is the repository, you can manage the business logic. So basic example would be the persistence, like demonstrated here

class SampleRepository(
    private val remoteDataSource: RemoteDataSource,
    private val localDataSource: LocalDataSource
) {
    suspend fun getUserInfo(): Result<UserModel> {
        // Fetch data from third party API
        val remoteResult = remoteDataSource.getUserInfo()
        when (remoteResult) {
            is Result.Success -> {
                // When success replace the local database and return the result
                // You could also return the local data for a single source of truth pattern
                localDataSource.updateUserInfo(remoteResult.data)
                return Result.Success(remoteResult.data)
            }
            is Result.Error -> {
                // If error fallback to local database
                val localResult = localDataSource.getUserInfo()
                when (localResult) {
                    is Result.Success -> {
                        return Result.Success(localResult.data)
                    }
                    is Result.Error -> {
                        return Result.Error(localResult.exception)
                    }
                    is Result.Loading -> {
                        return Result.Loading
                    }
                }
            }
        }
    }
}

The diagram shown above is helpful, but this is more on the actual code, so you'll understand better. Hope this help!

Paperback answered 5/10, 2023 at 16:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.