Skip to content

Constraints

Constraints are shared Chronicle behavior. The shared docs cover constraint concepts, model-bound constraints, declarative constraints, and client-tabbed examples.

IConstraintBuilder (passed into IConstraint.define) can scope every constraint added through it to a narrower uniqueness dimension than the whole event store:

MemberScopes uniqueness checking per…
perEventSourceTypeEvent source type
perEventStreamTypeEvent stream type
perEventStreamIdEvent stream id

Combine any of the three; by default (none called) a constraint is checked globally across the whole event store:

import io.cratis.chronicle.constraints.Constraint
import io.cratis.chronicle.constraints.IConstraint
import io.cratis.chronicle.constraints.IConstraintBuilder
import io.cratis.chronicle.events.EventType
@EventType
data class ConstraintsBridgeUserRegistered(
val userId: String = "",
val email: String = ""
)
@Constraint
class ConstraintsBridgeUniqueEmail : IConstraint {
override fun define(builder: IConstraintBuilder) {
builder
.perEventSourceType()
.unique { unique ->
unique
.on(
ConstraintsBridgeUserRegistered::class,
ConstraintsBridgeUserRegistered::email
)
.withMessage("Email must be unique per event source type.")
}
}
}

Kotlin client: unique over several properties

Section titled “Kotlin client: unique over several properties”

Pass more than one property to on when the value that must be unique is the combination of them. Here a person is rejected only when both the first and the last name match an existing one; sharing just one of them is fine. ignoreCasing() applies to every property in the constraint:

import io.cratis.chronicle.constraints.Constraint
import io.cratis.chronicle.constraints.IConstraint
import io.cratis.chronicle.constraints.IConstraintBuilder
import io.cratis.chronicle.events.EventType
@EventType
data class ConstraintsBridgePersonRegistered(
val firstName: String = "",
val lastName: String = ""
)
@Constraint
class ConstraintsBridgeUniqueFullName : IConstraint {
override fun define(builder: IConstraintBuilder) {
builder.unique { unique ->
unique
.on(
ConstraintsBridgePersonRegistered::class,
ConstraintsBridgePersonRegistered::firstName,
ConstraintsBridgePersonRegistered::lastName
)
.ignoreCasing()
}
}
}

Call on once per event type, each with its own properties, to make one constraint span several event types. Calling on a second time for the same event type throws EventTypeAlreadyAddedToUniqueConstraint - list every property in one call - and a unique { } block that never calls on throws NoEventTypesAddedToUniqueConstraint.

When a constraint spans several event types that each have several properties, every type must list the same number of properties in the same order, because the kernel combines the values in declaration order.

Java client: unique over several properties

Section titled “Java client: unique over several properties”

Java names the properties as strings through UniqueConstraintBuilderJavaBridge; pass as many as make up the unique value:

import io.cratis.chronicle.constraints.Constraint;
import io.cratis.chronicle.constraints.IConstraint;
import io.cratis.chronicle.constraints.IConstraintBuilder;
import io.cratis.chronicle.events.EventType;
import io.cratis.chronicle.java.UniqueConstraintBuilderJavaBridge;
@EventType
record ConstraintsBridgePersonRegistered(String firstName, String lastName) {}
@Constraint
class ConstraintsBridgeUniqueFullName implements IConstraint {
@Override
public void define(IConstraintBuilder builder) {
builder.unique(unique -> {
UniqueConstraintBuilderJavaBridge
.on(unique, ConstraintsBridgePersonRegistered.class,
"firstName", "lastName")
.ignoreCasing();
});
}
}