Usage of create_if_missing creationPolicy versus a secondary 'statestore'

Hi, I was wondering what your thoughts are about the following:
We have an Axon based application/platform, which has to synchronize data from other systems. We used to have a construction in which we used a secondary ‘statestore’. For example, upon receiving Create/Update/Delete (jms) messages from an external system, the statestore was used to determine how this message should be processed. For example, if a Create message is received for the first time, a Create command is dispatched to the CommandGateway. However, Create messages that refer to entities that are already present in the statestore are ignored. Likewise, Delete messages are only processed if a corresponding entry exists in the statestore, etc. One advantage of using this mechanism might be, that we would generate aggregateIds, and thus we did not have to use ‘meaningful’ ids from another system/context as aggregateIds. However, we felt the downside of such a construction is that you end up with sort of a fragile point (for example, the statestore can get corrupted for some ugly reason).
So we are thinking of using the CREATE_IF_MISSING creation policy on the aggregate level, making the usage of a statestore redundant. This implies that we should then use imported ids as aggregate identifiers (instead of generated UUIDs), but we think that in our case this would not pose a problem. Moreover, we did some simulations we high loads and this solution outperformed the old solution with the statestore significantly. An example of how the aggregate is implemented is given below.

My Question : Do you see any problems/downsides with this approach ?

@Slf4j
@Aggregate
@NoArgsConstructor
public class OrganisationAggregate {
  @AggregateIdentifier
  private String id;

  private boolean deleted;

  private Organisation organisation;

  @CommandHandler
  @CreationPolicy(AggregateCreationPolicy.CREATE_IF_MISSING)
  public void handle(CreateOrganisationCommand cmd) {
    if (id == null || deleted) {
        apply(new OrganisationCreatedEvent(cmd.getId(), cmd.getName()));
    }
  }

  @CommandHandler
  public void handle(UpdateOrganisationCommand cmd) {
    Organisation updatedOrganisation = new Organisation(cmd.getId(), cmd.getName());
    if (!deleted && !updatedOrganisation.equals(organisation)) {
        apply(new OrganisationUpdatedEvent(cmd.getId(), cmd.getName()));
    }
  }

  @CommandHandler
  public void handle(DeleteOrganisationCommand cmd) {
    if (deleted) {
        log.debug("DeleteOrganisationCommand ignored, {}", cmd);
    } else {
        apply(new OrganisationDeletedEvent(cmd.getId()));
    }
  }

  @EventSourcingHandler
  public void on(OrganisationCreatedEvent event) {
    this.id = event.getId();
    this.organisation = new Organisation(event.getId(), event.getName());
    this.deleted = false;
  }

  @EventSourcingHandler
  public void on(OrganisationUpdatedEvent event) {
    this.organisation = new Organisation(event.getId(), event.getName());
  }

  @EventSourcingHandler
  public void on(OrganisationDeletedEvent event) {
    this.organisation = null;
    this.deleted = true;
  }
}
2 Likes