Annotation Interface ForcedEntityCreator


@Retention(RUNTIME) @Target({METHOD,CONSTRUCTOR,ANNOTATION_TYPE}) public @interface ForcedEntityCreator
Annotation that indicates that a method or constructor is a factory method for an event-sourced entity, forcing creation at all times.

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:

  1. A no-arguments, @EntityCreator annotated constructor or method, will always create the entity.
  2. An identifier-based, @EntityCreator annotated constructor or method, will create if an identifier can be resolved.
  3. An event-based, @EntityCreator annotated 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:

These handlers must check the entity's state themselves, for example:

 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 Elements
    Modifier and Type
    Optional Element
    Description
    The qualified names of the payload types that this factory method can handle.
  • Element Details

    • payloadQualifiedNames

      String[] payloadQualifiedNames
      The 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 the MessageTypeResolver.
      Returns:
      The qualified names of the payload types that this factory method can handle.
      Default:
      {}