Last active
July 29, 2020 06:42
-
-
Save fteychene/8e525d4e7e44838ce366d7852e97e1e7 to your computer and use it in GitHub Desktop.
Hexagonal architecture as polymorphic monad stack in Kotlin with Arrow
This file contains hidden or 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
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile | |
plugins { | |
kotlin("jvm") version "1.3.21" | |
kotlin("kapt") version "1.3.21" | |
} | |
repositories { | |
mavenCentral() | |
} | |
val arrowVersion = "0.9.0" | |
dependencies { | |
implementation(kotlin("stdlib")) | |
compile("io.arrow-kt:arrow-core-data:$arrowVersion") | |
compile("io.arrow-kt:arrow-core-extensions:$arrowVersion") | |
compile("io.arrow-kt:arrow-syntax:$arrowVersion") | |
compile("io.arrow-kt:arrow-typeclasses:$arrowVersion") | |
compile("io.arrow-kt:arrow-extras-data:$arrowVersion") | |
compile("io.arrow-kt:arrow-extras-extensions:$arrowVersion") | |
kapt("io.arrow-kt:arrow-meta:$arrowVersion") | |
compile("io.arrow-kt:arrow-effects-data:$arrowVersion") | |
compile("io.arrow-kt:arrow-effects-extensions:$arrowVersion") | |
compile("io.arrow-kt:arrow-effects-io-extensions:$arrowVersion") | |
compile("io.arrow-kt:arrow-optics:$arrowVersion") | |
compile("io.arrow-kt:arrow-effects-rx2-data:$arrowVersion") | |
compile("io.arrow-kt:arrow-effects-rx2-extensions:$arrowVersion") | |
compile("io.reactivex.rxjava2:rxjava:2.2.8") | |
compile("io.arrow-kt:arrow-effects-reactor-data:$arrowVersion") | |
compile("io.arrow-kt:arrow-effects-reactor-extensions:$arrowVersion") | |
compile("io.projectreactor:reactor-core:3.2.8.RELEASE") | |
testCompile("junit", "junit", "4.12") | |
} | |
configure<JavaPluginConvention> { | |
sourceCompatibility = JavaVersion.VERSION_1_8 | |
} | |
tasks.withType<KotlinCompile> { | |
kotlinOptions.jvmTarget = "1.8" | |
} |
This file contains hidden or 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
import arrow.core.Either | |
import arrow.core.extensions.id.applicative.just | |
import arrow.core.right | |
import arrow.data.EitherT | |
import arrow.data.extensions.eithert.monad.monad | |
import arrow.data.fix | |
import arrow.effects.IO | |
import arrow.effects.extensions.io.monadDefer.monadDefer | |
import arrow.effects.fix | |
import arrow.effects.reactor.FluxK | |
import arrow.effects.reactor.MonoK | |
import arrow.effects.reactor.ForMonoK | |
import arrow.effects.reactor.extensions.fluxk.monadDefer.monadDefer | |
import arrow.effects.reactor.extensions.monok.monadDefer.monadDefer | |
import arrow.effects.reactor.fix | |
import arrow.effects.reactor.k | |
import arrow.effects.rx2.ObservableK | |
import arrow.effects.rx2.SingleK | |
import arrow.effects.rx2.extensions.observablek.monadDefer.monadDefer | |
import arrow.effects.rx2.extensions.singlek.monadDefer.monadDefer | |
import arrow.effects.rx2.fix | |
import arrow.effects.typeclasses.MonadDefer | |
import reactor.core.publisher.Mono | |
/** | |
* Domain | |
*/ | |
sealed class Error | |
class Domain<F>(val monadDefer: MonadDefer<F>, | |
val dataFetcher: DataFetcher<F> = GenericDataFetcher(monadDefer), | |
val repository: Repository<F> = GenericRepository(monadDefer), | |
val logger: Logger<F> = ConsoleLogger(monadDefer)) | |
fun <F> domainLogic(domain: Domain<F>, initialValue: Int): EitherT<F, Error, String> = | |
domain.run { | |
EitherT.monad<F, Error>(monadDefer).run { | |
binding { | |
logger.log("Load value").bind() | |
val result = dataFetcher.loadValues(initialValue) | |
.map { it * 2 }.bind() | |
logger.log("Insert in database").bind() | |
repository.persist(result).bind() | |
}.fix() | |
} | |
} | |
/** | |
* Ports | |
*/ | |
interface Logger<F> { | |
fun log(message: String): EitherT<F, Error, Unit> | |
} | |
interface DataFetcher<F> { | |
fun loadValues(id: Int): EitherT<F, Error, Int> | |
} | |
interface Repository<F> { | |
fun persist(value: Int): EitherT<F, Error, String> | |
} | |
/** | |
* Infra | |
*/ | |
class ConsoleLogger<F>(MDF: MonadDefer<F>) : Logger<F>, MonadDefer<F> by MDF { | |
override fun log(message: String): EitherT<F, Error, Unit> = | |
EitherT.liftF(this, delay { println(message) }) | |
} | |
class GenericDataFetcher<F>(MDF: MonadDefer<F>) : DataFetcher<F>, MonadDefer<F> by MDF { | |
override fun loadValues(id: Int): EitherT<F, Error, Int> = | |
EitherT.just(this, id) | |
} | |
class GenericRepository<F>(MDF: MonadDefer<F>) : Repository<F>, MonadDefer<F> by MDF { | |
override fun persist(value: Int): EitherT<F, Error, String> = | |
EitherT.just(this, value.toString()) | |
} | |
class SpecificReactorMonoRepository : Repository<ForMonoK> { | |
override fun persist(value: Int): EitherT<ForMonoK, Error, String> = | |
EitherT(Mono.just(value.right() as Either<Error, String>).k()) | |
} | |
/** | |
* Program | |
*/ | |
fun main() { | |
println("Run with Arrow IO") | |
val io = domainLogic(Domain(IO.monadDefer()), 60).value().fix() | |
io.attempt().unsafeRunAsync { println(it) } | |
println() | |
println("Run with RxJava Single") | |
val single = domainLogic(Domain(SingleK.monadDefer()), 60).value().fix() | |
single.single.subscribe(::println, ::println) | |
println() | |
println("Run with RxJava Observable") | |
val observable = domainLogic(Domain(ObservableK.monadDefer()), 60).value().fix() | |
observable.observable.subscribe(::println, ::println) | |
println() | |
println("Run with Reactor Mono") | |
val mono = domainLogic(Domain(MonoK.monadDefer(), repository = SpecificReactorMonoRepository()), 60).value().fix() | |
mono.mono.subscribe(::println, ::println) | |
println() | |
println("Run with Reactor Flux") | |
val flux = domainLogic(Domain(FluxK.monadDefer()), 60).value().fix() | |
flux.flux.subscribe(::println, ::println) | |
println() | |
} |
This file contains hidden or 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
Run with Arrow IO | |
Load value | |
Insert in database | |
Right(b=Right(b=Right(b=120))) | |
Run with RxJava Single | |
Load value | |
Insert in database | |
Right(b=120) | |
Run with RxJava Observable | |
Load value | |
Insert in database | |
Right(b=120) | |
Run with Reactor Mono | |
Load value | |
Insert in database | |
Right(b=120) | |
Run with Reactor Flux | |
Load value | |
Insert in database | |
Right(b=120) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment