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

no.ks.kes.test.example.Engine.kt Maven / Gradle / Ivy

The newest version!
package no.ks.kes.test.example

import no.ks.kes.lib.*
import no.ks.kes.serdes.jackson.JacksonCmdSerdes
import no.ks.kes.serdes.jackson.JacksonEventSerdes
import java.time.Duration
import java.time.Instant
import java.util.*

/**
 * All example code are considered to be part of the documentation and may be changed at any time without past notice
 */
private val LOG = mu.KotlinLogging.logger {  }

data class EngineProperties(val id: UUID, val running: Boolean, val startCount: Int = 0) : Aggregate

const val ENGINE_AGGREGATE_TYPE = "Engine"
const val SAGA_SERILIZATION_ID = "EngineSaga"

data class EngineSagaState(val aggregateId: UUID, val startInitiated: Boolean, val stoppedBySaga: Boolean = false)

object EngineSaga : Saga(EngineSagaState::class, SAGA_SERILIZATION_ID) {
    init {
        init { _: Events.Created, aggregateId: UUID ->
            LOG.debug { "Saga created: $aggregateId" }
            dispatch(Cmds.Start(aggregateId = aggregateId))
            setState(EngineSagaState(aggregateId = aggregateId, startInitiated = true))
        }

        apply { _: Events.Stopped, aggregateId: UUID ->
            LOG.debug { "Saga handles Stopped: $aggregateId" }
            setState(state.copy(startInitiated = false))
        }

        timeout({ _: Events.Started, aggregateId: UUID -> aggregateId }, { Instant.now().plus(Duration.ofSeconds(5L)) }) {
            LOG.debug { "Saga timed: ${state.aggregateId}" }
            if (state.startInitiated) {
                setState(state.copy(stoppedBySaga = true))
                dispatch(Cmds.Stop(state.aggregateId))
            }
        }
    }
}

object Engine : AggregateConfiguration(ENGINE_AGGREGATE_TYPE) {

    init {
        init { _: Events.Created, aggregateId: UUID ->
            EngineProperties(id = aggregateId, running = false)
        }

        applyEvent { copy(running = true, startCount = startCount + 1) }

        apply { copy(running = false) }
    }

}

class EngineCmdHandler(repository: AggregateRepository) : CmdHandler(repository, Engine) {
    init {
        init {
            LOG.debug { "Create command: ${it.aggregateId}" }
            Result.Succeed(
                Event( eventData = Events.Created(it.aggregateId), aggregateId = it.aggregateId))
        }

        apply {
            LOG.debug { "Tries to start" }
            if (running) {
                Result.Succeed()
            } else {
                Result.Succeed(
                    Event( eventData = Events.Started(it.aggregateId), aggregateId = it.aggregateId)
                )
            }
        }

        apply {
            if (running) {
                Result.Succeed(
                    Event( eventData = Events.Stopped(it.aggregateId), aggregateId = it.aggregateId)
                )
            } else {
                Result.Fail(RuntimeException("Can not stop engine that has already been stopped"))
            }
        }

        apply {
            Result.Succeed()
        }
    }
}

abstract class EngineCommand(override val aggregateId: UUID) : Cmd

object Cmds {

    val all = setOf(Create::class, Start::class, Stop::class, Check::class)
    val serdes = JacksonCmdSerdes(all)

    @SerializationId("Created")
    data class Create(override val aggregateId: UUID) : EngineCommand(aggregateId)

    @SerializationId("Start")
    data class Start(override val aggregateId: UUID) : EngineCommand(aggregateId)

    @SerializationId("Stop")
    data class Stop(override val aggregateId: UUID) : EngineCommand(aggregateId)

    @SerializationId("Check")
    data class Check(override val aggregateId: UUID) : EngineCommand(aggregateId)
}

abstract class EngineEvent(val description: String) : EventData {
    val timestamp: Instant = Instant.now()
}

object Events {

    val all = setOf(Created::class, Started::class, Stopped::class)
    val serdes = JacksonEventSerdes(all)

    @SerializationId("Created")
    data class Created(val aggregateId: UUID) : EngineEvent("Created engine with id $aggregateId")

    @SerializationId("Started")
    data class Started(val aggregateId: UUID) : EngineEvent("Engine $aggregateId started")

    @SerializationId("Stopped")
    data class Stopped(val aggregateId: UUID) : EngineEvent("Engine $aggregateId stopped")
}

class EnginesProjection: Projection() {
    private val enginesDefined = mutableSetOf()
    private val runningEngines = mutableSetOf()
    private val stoppedEngines = mutableSetOf()
    val all: Set
        get() = enginesDefined.toSet()

    val allRunning: Set
        get() = runningEngines.toSet()

    val allStopped: Set
        get() = stoppedEngines.toSet()

    init {
        on {
            enginesDefined.plusAssign(it.aggregateId)
        }
        on {
            runningEngines.plusAssign(it.aggregateId)
            stoppedEngines.minusAssign(it.aggregateId)
        }
        on {
            stoppedEngines.plusAssign(it.aggregateId)
            runningEngines.minusAssign(it.aggregateId)
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy