no.ks.kes.lib.AggregateConfiguration.kt Maven / Gradle / Ivy
package no.ks.kes.lib
import java.util.*
import kotlin.reflect.KClass
interface Aggregate
abstract class AggregateConfiguration(val aggregateType: String) {
protected val applicators: MutableMap>, (STATE, EventWrapper<*>) -> STATE> = mutableMapOf()
protected val initializers: MutableMap>, (EventWrapper<*>) -> STATE> = mutableMapOf()
@Suppress("UNCHECKED_CAST")
protected inline fun > apply(crossinline applicator: STATE.(E) -> STATE) {
applicators[E::class as KClass>] = { s, e -> applicator(s, e.event.eventData as E) }
}
@Suppress("UNCHECKED_CAST")
protected inline fun > init(crossinline initializer: (E, UUID) -> STATE) {
initializers[E::class as KClass>] = { initializer(it.event.eventData as E, it.event.aggregateId) }
}
@Suppress("UNCHECKED_CAST")
protected inline fun > applyEvent(crossinline applicator: STATE.(Event) -> STATE) {
applicators[E::class as KClass>] = { s, e ->
applicator(s, e.event as Event)
}
}
@Suppress("UNCHECKED_CAST")
protected inline fun > initEvent(crossinline initializer: (Event, UUID) -> STATE) {
initializers[E::class as KClass>] = { initializer(it.event as Event, it.event.aggregateId) }
}
@Suppress("UNCHECKED_CAST")
protected inline fun > applyWrapper(crossinline applicator: STATE.(EventWrapper) -> STATE) {
applicators[E::class as KClass>] = { s, e ->
applicator(s, e as EventWrapper)
}
}
@Suppress("UNCHECKED_CAST")
protected inline fun > initWrapper(crossinline initializer: (EventWrapper, UUID) -> STATE) {
initializers[E::class as KClass>] = { initializer(it as EventWrapper, it.event.aggregateId) }
}
fun getConfiguration(serializationIdFunction: (KClass>) -> String): ValidatedAggregateConfiguration =
ValidatedAggregateConfiguration(
aggregateType = aggregateType,
applicators = applicators,
initializers = initializers,
serializationIdFunction = serializationIdFunction
)
}
class ValidatedAggregateConfiguration(
val aggregateType: String,
applicators: Map>, (STATE, EventWrapper<*>) -> STATE>,
initializers: Map>, (EventWrapper<*>) -> STATE>,
serializationIdFunction: (KClass>) -> String
) {
private val applicators: Map) -> STATE>
private val initializers: Map) -> STATE>
init {
val duplicateApplicators = applicators.keys.map { serializationIdFunction.invoke(it) }.groupBy { it }.filter { it.value.size > 1 }.map { it.key }
check(duplicateApplicators.isEmpty()) { "There are multiple \"apply\" configurations for the event-types $duplicateApplicators in the configuration for $aggregateType, only a single \"apply\" handler is allowed for each event type" }
this.applicators = applicators.map { serializationIdFunction.invoke(it.key) to it.value }.toMap()
val duplicateInitializers = initializers.keys.map { serializationIdFunction.invoke(it) }.groupBy { it }.filter { it.value.size > 1 }.map { it.key }
check(duplicateApplicators.isEmpty()) { "There are multiple \"init\" configurations for the event-types $duplicateInitializers in the configuration for $aggregateType, only a single \"init\" handler is allowed for each event type" }
this.initializers = initializers.map { serializationIdFunction.invoke(it.key) to it.value }.toMap()
}
internal fun > applyEvent(wrapper: EventWrapper, currentState: STATE?): STATE? {
return if (currentState == null) {
val initializer = initializers[wrapper.serializationId]
if(initializer == null && applicators.containsKey(wrapper.serializationId))
error("Error reading ${aggregateType}(${wrapper.event.aggregateId}): event #${wrapper.eventNumber}(${wrapper.serializationId}) is configured as an applicator in the $aggregateType configuration, but the aggregate state has not yet been initialized. Please verify that an init event precedes this event in the event stream, or update your configuration")
initializer?.invoke(wrapper)
} else {
applicators[wrapper.serializationId]
?.invoke(currentState, wrapper)
?: currentState
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy