Skip to content

Instantly share code, notes, and snippets.

@idugalic
Created February 18, 2022 22:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save idugalic/232bc21490f4a8976bc31e0acf548831 to your computer and use it in GitHub Desktop.
Save idugalic/232bc21490f4a8976bc31e0acf548831 to your computer and use it in GitHub Desktop.
Kotlin’s context receivers - context-dependent declarations in Kotlin
package com.fraktalio.application.demo
import com.fraktalio.fmodel.application.EventRepository
import com.fraktalio.fmodel.application.StateRepository
import com.fraktalio.fmodel.domain.Decider
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.fold
// ##########################################################################
// ####################### Kotlin’s context receivers #######################
// ##########################################################################
// Context receivers enables context-dependent declarations in Kotlin.
// It enables composing contexts without using inheritance, which can simplify the `application` layer of the systems / orchestrates the execution of the logic.
// This demo is using FModel library which provides tactical Domain-Driven Design patterns, optimised for Event Sourcing, CQRS and streaming.
// https://github.com/fraktalio/fmodel
// https://fraktalio.com/blog/the-template.html
/**
* Event-sourced algorithm
* Computes new Events based on the previous Events and the Command.
*/
context (Decider<C, S, E>, C)
private fun <C, S, E> Flow<E>.computeNewEvents(): Flow<E> = flow {
val currentState = fold(initialState) { s, e -> evolve(s, e) }
val resultingEvents = decide(this@C, currentState)
emitAll(resultingEvents)
}
/**
* State-stored algorithm
* Computes new State based on the previous State and the Command.
*/
context (Decider<C, S, E>, C)
private suspend fun <C, S, E> S?.computeNewState(): S {
val currentState = this ?: initialState
val events = decide(this@C, currentState)
return events.fold(currentState) { s, e -> evolve(s, e) }
}
/**
* Publish command - Event-sourced decider
*/
context (Decider<C, S, E>, EventRepository<C, E>)
fun <C, S, E> C.publish(): Flow<E> = fetchEvents().computeNewEvents().save()
/**
* Publish command - State-stored decider
*/
context (Decider<C, S, E>, StateRepository<C, S>)
suspend fun <C, S, E> C.publish(): S = fetchState().computeNewState().save()
// ##############################################################
// ###################### build.gradle.kts ######################
// ##############################################################
//import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
//
//plugins {
// kotlin("jvm") version "1.6.20-M1"
//}
//
//tasks.withType<KotlinCompile>().configureEach {
// kotlinOptions {
// freeCompilerArgs = freeCompilerArgs + "-Xcontext-receivers"
// }
//}
//
//group = "com.fraktalio.application.demo"
//version = "1.0.0-SNAPSHOT"
//
//repositories {
// mavenCentral()
//}
//
//dependencies {
// implementation(kotlin("stdlib"))
// implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
// implementation("com.fraktalio.fmodel:application:3.0.0")
//}
// #####################################################################
// ########## Latest Kotlin EAP release 1.6.20-M1 is required ##########
// #####################################################################
// - https://kotlinlang.org/docs/install-eap-plugin.html
// ######################################################################
// ################### References and further reading ###################
// ######################################################################
// - https://blog.jetbrains.com/kotlin/2022/02/kotlin-1-6-20-m1-released/
// - https://github.com/Kotlin/KEEP/blob/master/proposals/context-receivers.md#detailed-design
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment