
org.elder.sourcerer.kotlin.EventStreams.kt Maven / Gradle / Ivy
package org.elder.sourcerer.kotlin
import org.elder.sourcerer.AggregateRepository
import org.elder.sourcerer.CommandFactory
import org.elder.sourcerer.CommandResponse
import org.elder.sourcerer.DefaultCommandFactory
import org.elder.sourcerer.ExpectedVersion
import org.elder.sourcerer.ImmutableAggregate
import org.elder.sourcerer.OperationHandler
import org.elder.sourcerer.OperationHandlerOperation
import org.elder.sourcerer.Operations
import org.elder.sourcerer.functions.UpdateHandlerAggregate
/**
* Allows for creating, updating or appending to eventstore streams.
*
* Kotlin wrapper for CommandFactory that simplifies its usage.
*/
class EventStreams(
private val commandFactory: CommandFactory
) {
/**
* Create EventStreams that uses a DefaultCommandFactory.
*/
constructor(
aggregateRepository: AggregateRepository
) : this(DefaultCommandFactory(aggregateRepository))
/**
* Create a new stream.
*
* @param id the aggregate id
* @param conflictStrategy what to do if aggregate already exists. Default is to fail
* @param create operations to perform on the aggregate
*/
fun create(
id: String,
conflictStrategy: CreateConflictStrategy = CreateConflictStrategy.FAIL,
create: (ImmutableAggregate) -> ImmutableAggregate
): CommandResponse {
return CommandResponse.of(
commandFactory
.fromOperation(constructOf(UpdateHandlerAggregate { state ->
create(state)
}))
.setAggregateId(id)
.setAtomic(true)
.setExpectedVersion(ExpectedVersion.notCreated())
.setIdempotentCreate(conflictStrategy.idempotentCreate)
.run())
}
/**
* Update existing stream.
*
* @param id the aggregate id
* @param expectedVersion expected aggregate version. By default will fail if stream does not
* exist
* @param update operations to perform on the aggregate
*/
fun update(
id: String,
expectedVersion: ExpectedVersion = ExpectedVersion.anyExisting(),
update: (ImmutableAggregate) -> ImmutableAggregate
): CommandResponse {
return CommandResponse.of(
commandFactory
.fromOperation(updateOf(update, expectedVersion))
.setAggregateId(id)
.setAtomic(true)
.setExpectedVersion(expectedVersion)
.run())
}
/**
* Append events to stream.
*
* Stream may be new or existing. No validation is performed, and nothing is read.
*
* @param id the aggregate id
* @param operation operations to perform to create events
*/
fun append(id: String, operation: () -> List): CommandResponse {
return CommandResponse.of(
commandFactory
.fromOperation(Operations.appendOf(operation))
.setAggregateId(id)
.run())
}
private fun updateOf(
update: (ImmutableAggregate) -> ImmutableAggregate,
expectedVersion: ExpectedVersion
): OperationHandlerOperation {
return OperationHandlerOperation(
OperationHandler { agg, _ -> update(agg!!).events() },
true,
false,
expectedVersion,
true)
}
private fun constructOf(
operation: UpdateHandlerAggregate
): OperationHandlerOperation {
return OperationHandlerOperation(
operation,
true,
false,
ExpectedVersion.notCreated(),
true)
}
}
/**
* Strategy for when trying to create an aggregate that already exists.
*/
enum class CreateConflictStrategy(internal val idempotentCreate: Boolean) {
/**
* Throw UnexpectedVersionException.
*/
FAIL(idempotentCreate = false),
/**
* Do nothing.
*/
NOOP(idempotentCreate = true);
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy