com.cultureamp.eventsourcing.CommandGateway.kt Maven / Gradle / Ivy
package com.cultureamp.eventsourcing
interface CommandGateway {
companion object {
operator fun invoke(eventStore: EventStore, vararg routes: Route<*, *, M>) = EventStoreCommandGateway(eventStore, *routes)
}
fun dispatch(command: Command, metadata: M, retries: Int = 5): Either
}
class EventStoreCommandGateway(private val eventStore: EventStore, private vararg val routes: Route<*, *, M>) : CommandGateway {
override tailrec fun dispatch(command: Command, metadata: M, retries: Int): Either {
val result = createOrUpdate(command, metadata)
return if (result is Left && result.error is RetriableError && retries > 0) {
Thread.sleep(500L)
dispatch(command, metadata, retries - 1)
} else {
result
}
}
private fun createOrUpdate(command: Command, metadata: M): Either {
val constructor = constructorFor(command) ?: return Left(NoConstructorForCommand)
val events = eventStore.eventsFor(command.aggregateId)
return if (events.isEmpty()) when (command) {
is CreationCommand -> constructor.create(command, metadata, eventStore).map { Created }
else -> Left(AggregateNotFound)
} else when (command) {
is UpdateCommand -> constructor.update(command, metadata, events, eventStore).map { Updated }
else -> Left(AggregateAlreadyExists)
}
}
@Suppress("UNCHECKED_CAST")
private fun constructorFor(command: Command): AggregateConstructor>? {
val route = routes.find { it.creationCommandClass.isInstance(command) || it.updateCommandClass.isInstance(command) }
return route?.aggregateConstructor as AggregateConstructor>?
}
}
sealed class SuccessStatus
object Created : SuccessStatus()
object Updated : SuccessStatus()
object NoConstructorForCommand : CommandError
object AggregateAlreadyExists : CommandError
object AggregateNotFound : CommandError
© 2015 - 2025 Weber Informatics LLC | Privacy Policy