Created
December 24, 2019 05:50
-
-
Save khunzohn/b77174bc28a44c3eeb958568b8e5f3d9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class FetchMacsUseCase constructor( | |
private val productRepository: ProductRepository | |
) { | |
fun run(): Observable<MacsPartialState> { | |
return productRepository.fetchMacs() | |
.toObservable() | |
.map { MacsPartialState.MacsResult(it) } | |
.cast(MacsPartialState::class.java) | |
.startWith(MacsPartialState.LoadingMacs) | |
.onErrorReturn { error -> MacsPartialState.LoadMacsError(error) } | |
} | |
} | |
class StreamMacsUseCase constructor( | |
private val productRepository: ProductRepository | |
) { | |
fun run(): Observable<MacsPartialState> { | |
return productRepository.streamMacs() | |
.map { MacsPartialState.MacsResult(it) } | |
// loading is not required for streaming data from db and let's assume there's no error case | |
} | |
} | |
interface ProductRepository { | |
fun fetchMacs(): Single<List<Mac>> | |
fun streamMacs(): Observable<List<Mac>> | |
} | |
class ProductRepositoryImpl constructor( | |
private val productRemote: ProductRemote, | |
private val productLocal: ProductLocal | |
) : ProductRepository { | |
override fun fetchMacs(): Single<List<Mac>> { | |
return productRemote.getMacs() | |
.doOnSuccess { macs -> | |
productLocal.saveMacs(macs) | |
} | |
} | |
override fun streamMacs(): Observable<List<Mac>> { | |
return productLocal.streamMacs() | |
} | |
} | |
interface ProductRemote { | |
fun getMacs(): Single<List<Mac>> | |
} | |
class ProductRemoteImpl(private val mapper: MacMapper) : ProductRemote { | |
override fun getMacs(): Single<List<Mac>> { | |
TODO("Implementation detail for fetching macs from network") | |
// Mapping from network model to domain model happens here | |
} | |
} | |
interface ProductLocal { | |
fun saveMacs(macs: List<Mac>) | |
fun streamMacs(): Observable<List<Mac>> | |
} | |
class ProductLocalImpl( | |
private val macDao: MacDao, | |
private val mapper: MacMapper | |
) : ProductLocal { | |
override fun saveMacs(macs: List<Mac>) { | |
val entities: List<MacEntity> = macs.map { mapper.domainToEntity(it) } | |
TODO("Implementation detail for saving mac entities to the local db") | |
} | |
override fun streamMacs(): Observable<List<Mac>> { | |
return macDao.stream().map { macEntity -> | |
mapper.entityToDomain(macEntity) | |
} | |
} | |
} | |
class MacMapper { | |
fun networkToDomain(network: MacData): Mac { | |
TODO() | |
} | |
fun domainToEntity(domain: Mac): MacEntity { | |
TODO() | |
} | |
fun entityToDomain(entity: MacEntity): Mac { | |
TODO() | |
} | |
} | |
//domain model | |
data class Mac( | |
val id: String, | |
val imageUrl: String, | |
val name: String | |
) | |
// db model | |
data class MacEntity( | |
val id: String, | |
val imageUrl: String, | |
val name: String | |
) | |
//network model | |
data class MacData( | |
val id: String, | |
val imageUrl: String, | |
val name: String | |
) | |
class MacsViewModel( | |
private val streamMacsUseCase: StreamMacsUseCase, | |
private val fetchMacsUseCase: FetchMacsUseCase | |
) { | |
// intent fired by user or view | |
private val streamMacsIntent = PublishSubject.create<Any>() | |
private val fetchMacsIntent = PublishSubject.create<Any>() | |
//this will be subscribed by the View | |
private val viewStateSubject = BehaviorSubject.create<MacsViewState>() | |
fun handleIntent() { | |
val streamMacsPartialState: Observable<MacsPartialState> = streamMacsIntent | |
.switchMap { streamMacsUseCase.run() } | |
val fetchMacsPartialState: Observable<MacsPartialState> = fetchMacsIntent | |
.switchMap { fetchMacsUseCase.run() } | |
Observable.merge( | |
streamMacsPartialState, | |
fetchMacsPartialState | |
) | |
.scan(MacsViewState()) { oldState: MacsViewState, partialState: MacsPartialState -> | |
partialState.reduce(oldState) | |
} | |
.distinctUntilChanged() | |
.subscribe { | |
viewStateSubject.onNext(it) | |
} | |
} | |
} | |
data class MacsViewState( | |
val macs: List<Mac> = emptyList(), | |
val loadingMacs: Boolean = false, | |
val loadMacsError: Throwable? = null | |
) | |
sealed class MacsPartialState { | |
abstract fun reduce(oldState: MacsViewState): MacsViewState | |
object LoadingMacs : MacsPartialState() { | |
override fun reduce(oldState: MacsViewState): MacsViewState { | |
return MacsViewState( | |
macs = oldState.macs, | |
loadingMacs = true, | |
loadMacsError = oldState.loadMacsError | |
) | |
} | |
} | |
object MacsLoaded : MacsPartialState() { | |
override fun reduce(oldState: MacsViewState): MacsViewState { | |
return MacsViewState( | |
macs = oldState.macs, | |
loadingMacs = false, | |
loadMacsError = oldState.loadMacsError | |
) | |
} | |
} | |
data class MacsResult(val macs: List<Mac>) : MacsPartialState() { | |
override fun reduce(oldState: MacsViewState): MacsViewState { | |
return MacsViewState( | |
macs = macs, | |
loadingMacs = oldState.loadingMacs, | |
loadMacsError = oldState.loadMacsError | |
) | |
} | |
} | |
data class LoadMacsError(val error: Throwable) : MacsPartialState() { | |
override fun reduce(oldState: MacsViewState): MacsViewState { | |
return MacsViewState( | |
macs = oldState.macs, | |
loadingMacs = oldState.loadingMacs, | |
loadMacsError = error | |
) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment