All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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