PrepareKey

interface PrepareKey<V : Any> : Named

Key preparation interface for ensuring uniqueness constraints in EventSourcing architectures.

Unlike traditional databases that use UNIQUE KEY constraints, EventSourcing requires application-level uniqueness guarantees. PrepareKey provides a mechanism to "prepare" or reserve keys before they're actually used, ensuring atomicity and preventing race conditions.

Key features:

  • Atomic key preparation and rollback

  • TTL (Time-To-Live) support for temporary reservations

  • Reprepare operations for key changes

  • Transaction-like semantics with automatic rollback on failure

Common use cases:

  • Username/email uniqueness during user registration

  • Product SKU uniqueness

  • Resource identifier allocation

  • Preventing duplicate operations

Parameters

V

The type of value associated with prepared keys

Example usage for user registration:

@AggregateRoot
class User(private val state: UserState) {
@OnCommand
private fun onRegister(
register: Register,
passwordEncoder: PasswordEncoder,
usernamePrepare: PrepareKey<UsernameIndexValue>,
): Mono<Registered> {
val encodedPassword = passwordEncoder.encode(register.password)
return usernamePrepare.usingPrepare(
key = register.username,
value = UsernameIndexValue(
userId = state.id,
password = encodedPassword,
),
) {
require(it) {
"username[${register.username}] is already registered."
}
Registered(username = register.username, password = encodedPassword).toMono()
}
}
}

Example usage for changing username:

@OnCommand
private fun onChangeUsername(
changeUsername: ChangeUsername,
usernamePrepare: PrepareKey<UsernameIndexValue>
): Mono<UsernameChanged> {
val usernameIndexValue = UsernameIndexValue(
userId = state.id,
password = state.password,
)
return usernamePrepare.reprepare(
oldKey = state.username,
oldValue = usernameIndexValue,
newKey = changeUsername.newUsername,
newValue = usernameIndexValue
).map {
require(it) {
"username[${changeUsername.newUsername}] is already registered."
}
UsernameChanged(username = changeUsername.newUsername)
}
}

See also

for value wrapper with TTL support

PrepareKeyProxyFactory

for proxy-based implementation

Properties

Link copied to clipboard
abstract val name: String

Functions

Link copied to clipboard
open fun get(key: String): Mono<V>

Retrieves a prepared value by key, filtering out expired entries.

Link copied to clipboard
abstract fun getValue(key: String): Mono<PreparedValue<V>>

Retrieves the full prepared value information including expiration status.

Link copied to clipboard
open fun prepare(key: String, value: V): Mono<Boolean>

Prepares a key with a value that never expires.

abstract fun prepare(key: String, value: PreparedValue<V>): Mono<Boolean>

Prepares a key with a value that may have a time-to-live.

Link copied to clipboard
open fun reprepare(key: String, value: V): Mono<Boolean>

Reprepares a key with a new permanent value.

abstract fun reprepare(key: String, value: PreparedValue<V>): Mono<Boolean>
abstract fun reprepare(key: String, oldValue: V, newValue: PreparedValue<V>): Mono<Boolean>

Reprepares a key with a new value that may have TTL.

open fun reprepare(key: String, oldValue: V, newValue: V): Mono<Boolean>

Reprepares a key with a new value that never expires.

open fun reprepare(oldKey: String, oldValue: V, newKey: String, newValue: V): Mono<Boolean>

Reprepares by changing both key and value to new permanent values.

open fun reprepare(oldKey: String, oldValue: V, newKey: String, newValue: PreparedValue<V>): Mono<Boolean>

Reprepares by changing both key and value with optional TTL.

Link copied to clipboard
abstract fun rollback(key: String): Mono<Boolean>

Rolls back any prepared value for the given key.

abstract fun rollback(key: String, value: V): Mono<Boolean>

Rolls back a prepared key only if it matches the specified value.

Link copied to clipboard
open fun <R> usingPrepare(key: String, value: V, then: (Boolean) -> Mono<R>): Mono<R>

Executes an operation within a prepare context with permanent value.

open fun <R> usingPrepare(key: String, value: PreparedValue<V>, then: (Boolean) -> Mono<R>): Mono<R>

Executes an operation within a prepare context with optional TTL.