Getting Started
This page assumes you’ve read What is Conta Fork Yeah? and The Forking Model.
Adding fork support to an entity
The minimal recipe to make a domain entity forkable.
The example below is a Ninja that lives under a Dojo (the root) and is keyed by season.
1. Define an IdentifierKey
Pick a short, stable key value.
public enum NinjaKey implements IdentifierKey {
Dojo("dj"),
Ninja("nj"),
;
private final String keyValue;
NinjaKey(String keyValue) { this.keyValue = keyValue; }
@Override public String keyValue() { return keyValue; }
@Override public IdentifierKey parentKey() {
return this == Dojo ? null : Dojo;
}
}
2. Add a ForkSources field
ForkSources is small and serialises cleanly as JSON; the typical persistence pattern is a JSON column:
@JdbcTypeCode(SqlTypes.JSON)
@Column(name = "forked_from")
private ForkSources forkedFrom;
3. Implement ForkAware
public class Ninja implements ForkAware<Ninja> {
@Override
public Identifier toIdentifier() {
return Identifier.of(NinjaKey.Ninja, getUuid());
}
@Override
public Optional<Identifier> resolveParentIdentifier() {
return Optional.ofNullable(getDojo()).map(Dojo::toIdentifier);
}
@Override
public ForkSources getForkedFrom() {
return forkedFrom;
}
@Override
public Forker<Ninja> fork() {
return new NinjaForker(this);
}
@Override
public Ninja toForkRef(@NonNull ForkMode mode) {
var ref = new Ninja();
ref.setUuid(getUuid());
ref.setForkedFrom(toForkSources(mode));
return ref;
}
}
toForkRef returns a minimal shell — UUID and lineage only — for use as a reference from a forked sibling.
4. Implement the Forker
public class NinjaForker implements Forker<Ninja> {
private final Ninja original;
public NinjaForker(Ninja original) { this.original = original; }
@Override
public Ninja toNext() {
var n = new Ninja();
n.setUuid(UUID.randomUUID());
n.setSeason(original.getSeason() + 1);
n.setForkedFrom(original.toForkSources(ForkMode.ToNext));
n.setDojo(ForkRefAware.ofNullable(original.getDojo(), ForkMode.ToNext));
n.setRank(original.getRank());
return n;
}
}
Implement only the modes you actually support.
The default toCopy/toNext/toPrevious throw UnsupportedOperationException, which is the correct behaviour for unsupported modes.
5. Define a fork command
Commands that initiate a fork implement ForkEnabled:
public class ForkNinjaCommand implements TenantCommand, ForkEnabled {
@NonNull @NotNull @Valid private ForkSource forkFrom;
private int season;
}
6. Wire it up in the service
public Ninja createFork(ForkNinjaCommand cmd) {
var forkFrom = ForkSource.valid(cmd.getForkFrom());
var source = repository.findById(forkFrom.getIdentifier().uuid1())
.orElseThrow(() -> Problem.of(ForkMode.toProblemCode(forkFrom))
.withInfo("forkFrom", forkFrom)
.toThrowable());
if (!forkFrom.getForkMode().sourceYearMatchesTargetYear(source.getSeason(), cmd.getSeason())) {
throw Problem.of(ForkMode.toProblemCode(forkFrom))
.withInfo("sourcePeriod", source.getSeason(), "targetPeriod", cmd.getSeason())
.toThrowable();
}
var ctx = ForkContext.of(forkFrom, cmd);
var forked = source.fork(forkFrom.getForkMode());
return repository.save(forked);
}
That’s the full minimal setup. For nested entities and partial-success handling, see Bulk Forking.