In our case, I need to perform request chain on application startup, for examples
- Application needs retrieve location by gis or IP address
- request a server list by location via http
- request remote configs from a server which in server list we got
I create a data layer for each item
class DefaultLocationRepository(
@LocateByGis gisLocationDataSource: LocationDataSource,
@LocateByIp ipLocationDataSource: LocationDataSource
): LocationRepository {
suspend fun locate(): Result<Location> {
....
}
}
class DefaultServerRepository(
remoteServerDataSource: RemoteServerDataSource
): ServerRepository {
suspend fun retrieve(location: Location): List<Server> {
}
}
class DefaultRemoteConfigRepository (
remoteConfigDataSource: RemoteConfigDataSource
): RemoteConfigDataSource {
suspend fun retrieve(server: List<Server>) {
}
}
My question is, what are the best practices for chain these tasks? I can make a use case for each action
class LocateUseCase
class RetrieveServersUseCase
class RetrieveRemoteConfigUseCase
class MainViewModel: ViewModel() {
suspend fun start() {
locateUserCase().onSuccess {
retrieveServerUseCase(GetLocationUseCase()).onSuccess {
retrieveConfigServerUseCase(GetServerListUseCase()).onSuccess {
// balabala...
}
}
}
}
}
I think It's too ugly
and I can do it by hilt injection, the repository returns a Flow instead of a suspend function call,
class LocatonRepository() {
private var latestLocation: Location? = null
val location: Flow<Location> {
if (latestLocation != null) {
emit (latestLocation!!)
return@flow
}
dataSource.get().onSuccess { emit(it) }
}
}
// Modules
@InstallIn(Singleton::class::java)
@Module
class Modules {
@Provides
fun provideLocation(repository: LocationRepository): Flow<Location> =
repository.location
}
when Flow inject into ServerList, server repository can trigger locate location requet by location.first()
but how to implement cancel/retry logic in flow chain? Or let ui known which step is wrong or stucked?
CodePudding user response:
what about this:
suspend fun start() {
val locationResult = locateUserCase()
if(locationResult.isFailure()) {
// do location error handling
return
}
val location = locationResult.getOrThrow()
val serverResult = retrieveServerUseCase(location)
if(serverResult.isFailure()) {
// do server error handling
return
}
val server = serverResult.getOrThrow()
val configResult = retrieveConfigServerUseCase(server)
if(configResult.isFailure()) {
// do config error handling
return
}
// do total success handling
}
You allow each of your cases to return a result, handle the error handling separately and return early to stop continuation. Shows a clear flow between areas, but isn't callback hell.