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!