简体   繁体   中英

Koin Dependency Injection switching between local and remote data source

So I am writing this app with remote data source. And I wanted to add local db storage capabilities. I have setup an architecture whereby I have an interface DataSource . A RemoteDataSource and a LocalDataSource classes implement that interface. The RemoteDataSource is injected with ApiInterface retrofit and the LocalDataSource is injected with DAOs.

Now there is this repository interface and implementation SomeDataRepository and SomeDataRepositoryImpl . If I want the repository to be able to fetch the data from api and save it to the database, how do I go about doing that?

Currently I have injected both RemoteDataSource and LocalDataSource classes to the SomeDataRepositoryImpl to access methods from different data sourcces. This way I can call something like localDataSource.saveToDb() and/or remoteDatSource.fetchSomeData() int SomeRepositoryImpl class. But I do not know if passing concrete implementations to a class is the way to go.

But if I pass lets say a single DataSource interface to the SomeDataRepository , I will have to define a saveToDb() function int the interface DataSource and then I will have to implement that in RemoteDataSource as well which is not that good.

Can anyone please guide me through what the best approach is to this solution.

And also while I am at it, is it any good to wrap the data with LiveData wrapper class right in the api interface for retrofit? cause I dont think when a method is called on the repository, I would want to observe it right there in the repo and then access the data to put it onto local db.

Since you want to have the local data source act as a fallback for the remote data source, you can create another data source implementation that is a composition of the local and remote data sources. This composite data source can contain the fallback logic and handle the delegation to the remote and local datasources as needed. Once you have done this, it is a simple matter to create a Koin module to construct these, and bind the composite data source to the data source interface.

Suppose this is your interface and the two data sources you already have:

interface DataSource {
    fun getData(): Data
}

class RemoteDataSource : DataSource {
    // ...
}

class LocalDataSource : DataSource {
    // ...
}

Then you can create a third implementation like this one:

class CompositeDataSource(
    val remote: RemoteDataSource, 
    val local: LocalDataSource
) : DataSource {
    override fun getData() : Data {
        return try {
            remote.getData()
        } catch (e: Exception) {
            local.getData()
        }
    }
}

To define all of this, your koin module would look something like this

module {
    single { RemoteDataSource() }
    single { LocalDataSource() }
    single<DataSource> { CompositeDataSource(remote = get(), local = get()) }
}

Edit: If what you actually want is a cache, you can use the local data source as your cache like this:

class CompositeDataSource(
    val remote: RemoteDataSource, 
    val local: LocalDataSource
) : DataSource {
    override fun getData() : Data {
        return try {
            remote.getData().also { local.saveData(it) }
        } catch (e: Exception) {
            local.getData()
        }
    }
}

You can try the next approch, it requirs minimal changes and it works for me:

Add interfaces for the remote and local data source, it should inherit the main DataSource interface

interface QuotesDataSource {

fun getQuotes(skip: Int = 0, force: Boolean = false): Flow<List<Quote>>

suspend fun updateQuotes(quotes: List<Quote>)
}

interface QuotesRemoteDataSource : QuotesDataSource

interface QuotesLocalDataSource : QuotesDataSource

Then use those interfaces to create koin module

val repoModule = module {
  single { QuotesApi() }
  single<QuotesLocalDataSource> { QuotesDatabase(get()) }
  single<QuotesRemoteDataSource> { QuotesRemote(get()) }
  single<QuotesDataSource> {
      QuotesRepository(
          local = get<QuotesLocalDataSource>(),
          remote = get<QuotesRemoteDataSource>()
      )
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM