Annotation Interface ForcedEntityCreator
It is meta-annotated with EntityCreator to support all behavior as described on the EntityCreator.
What this annotation adds, is the assurance that if sufficient information is available to invoke the
annotated method or constructor, that it will be invoked regardless of downstream usages of the entity itself. This
adjusts the regular behavior of the no-arguments, identifier-only, and event-based EntityCreator solutions
like so:
- A no-arguments,
@EntityCreatorannotated constructor or method, will always create the entity. - An identifier-based,
@EntityCreatorannotated constructor or method, will create if an identifier can be resolved. - An event-based,
@EntityCreatorannotated constructor or method, will only create if an initial event is present.
This additional behavior becomes important for create-if-missing styled handlers. For plain EntityCreator
solutions, the create-if-missing handler should either (1) be static (read: marked as a creational handler)
when present in the entity itself or (2) moved out of the entity entirely. In both scenarios the create-if-missing
behavior would require the entity as a parameter to the message handling function validate if it does exist. If
optional subsequent decisions require life state changes of the potentially missing entity, the user is enforced to
inject a ManagedEntity instead.
This annotation adjusts that behavior, by simply invoking the @ForcedEntityCreator annotated constructor or
method and passing it through. This will effectively make the entity non-null for all no-argument scenarios and most
identifier-based scenarios. Furthermore, it allows the create-if-missing solution to work on instance command
handlers placed in the entity, resolving the need to make them static.
Forced creation is not limited to create-if-missing handlers. When the entity has no events yet, every handler that receives the entity gets the just-constructed instance in its default state:
- Instance command handlers in the entity are loaded through
Repository.loadOrCreate(Object, org.axonframework.messaging.core.unitofwork.ProcessingContext). They run on the default state instead of failing with anEntityNotFoundException. InjectEntityparameters of command and event handlers outside the entity receive the default instance too. A@Nullableparameter is nevernull, anOptionalparameter is never empty, andManagedEntity.entity()is nevernull. A handler written as "create if the entity isnull, update otherwise" always takes the update branch.- Static creational command handlers are the exception. Their commands are loaded through
Repository.load(Object, org.axonframework.messaging.core.unitofwork.ProcessingContext), which does not invoke the creator without events, so the entity counts as absent and the command reaches the static handler.
public class GiftCard {
private final String cardId;
private boolean issued;
@ForcedEntityCreator
public GiftCard(@InjectEntityId String cardId) {
this.cardId = cardId;
}
@CommandHandler
public void handle(RedeemGiftCard command, EventAppender appender) {
if (!issued) {
throw new IllegalStateException("Gift card [" + cardId + "] has not been issued");
}
appender.append(new GiftCardRedeemed(cardId, command.amount()));
}
}
Forced creation only applies when the forced creator is the one selected. Among the creators that take no event,
the one with the most parameters is selected, so a plain identifier-based EntityCreator takes precedence
over a forced no-arguments creator. On an event-based creator this annotation has no effect without an event.
Note that this forced-creation-approach is viewed as an aggregate-centric solution, whereas this library aims to steer away from that. Hence, any opportunity to use other mechanisms than this are encouraged.
- Since:
- 5.3.1
- Author:
- Steven van Beelen
-
Optional Element Summary
Optional ElementsModifier and TypeOptional ElementDescriptionString[]The qualified names of the payload types that this factory method can handle.
-
Element Details
-
payloadQualifiedNames
String[] payloadQualifiedNamesThe qualified names of the payload types that this factory method can handle. If a payload parameter is declared, and this value is left at default, the payload's qualified name will be determined based on theMessageTypeResolver.- Returns:
- The qualified names of the payload types that this factory method can handle.
- Default:
{}
-