Created
April 14, 2023 09:33
-
-
Save rcd27/a24f0508c532bd698b6a91d19cd79117 to your computer and use it in GitHub Desktop.
I'm not against Mono<A> + Result<C>, but supsend + Either is also good choice
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
package com.github.redarmour.pantheon.maxim | |
import arrow.core.Either | |
import arrow.core.continuations.either | |
import kotlinx.coroutines.coroutineScope | |
import kotlinx.coroutines.delay | |
/** | |
* На самом деле предлагаемый подход - суть то же, что не suspend функции, которые возврашают Mono<Result<T>> | |
* За тем лишь исключением, что котлинячий Result не "стакается" так же, как Either.Left | |
*/ | |
suspend fun dataUpdateProcess(unvalidatedUpdateRequest: UnvalidatedDataUpdateRequest): Either<DataUpdateError, SubscriberDataUpdateResponse> = | |
either { | |
val validated: ValidatedDataUpdateRequest = ValidatedDataUpdateRequest.emerge(unvalidatedUpdateRequest).bind() | |
val existingData = findDataWithUpdates(validated).bind() // <-- bind() = flatMap в скоупе Either | |
/* | |
... | |
*/ | |
SubscriberDataUpdateResponse.fromExistingData(existingData) | |
} | |
suspend fun findDataWithUpdates(input: ValidatedDataUpdateRequest): Either<NoDataFound, DataUpdate> = coroutineScope { | |
delay(1000) // <-- Асинхронщина, пожалуйста. Reactive Streams на Mono<T> - аналог suspend функций, как по мне | |
Either.catch { | |
throw RuntimeException("No data found for: $input") | |
} | |
.mapLeft { | |
NoDataFound(it) | |
} | |
} | |
data class UnvalidatedDataUpdateRequest( | |
val x: String?, | |
val y: String? | |
) | |
data class SubscriberDataUpdateResponse internal constructor(val responseBody: String) { | |
companion object { | |
fun fromExistingData(source: DataUpdate): SubscriberDataUpdateResponse { | |
return SubscriberDataUpdateResponse(source.toString()) | |
} | |
} | |
} | |
data class ValidatedDataUpdateRequest internal constructor( | |
val validatedX: String, | |
val validatedY: String | |
) { | |
companion object { | |
fun emerge(input: UnvalidatedDataUpdateRequest): Either<ValidationError, ValidatedDataUpdateRequest> { | |
return Either.catch { | |
require(input.x != null) | |
require(input.y != null) | |
ValidatedDataUpdateRequest(input.x, input.y) | |
} | |
.mapLeft { | |
// TODO: тут описать алгебру валидации | |
ValidationError(it) | |
} | |
} | |
} | |
} | |
/** | |
* Вообще в принципе любая доменная ошибка | |
*/ | |
sealed interface DataUpdateError | |
data class ValidationError(val t: Throwable) : DataUpdateError | |
/** | |
* Для быстроты я прокидываю сюда Throwable, но что будет в теле доменной ошибки - не суть важно | |
*/ | |
data class NoDataFound(val t: Throwable) : DataUpdateError | |
data class DataUpdate( | |
val xToUpdate: String, | |
val yToUpdate: String | |
) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment