1
votes

L'identifiant d'agrégat doit être non nul après l'application d'un événement. Assurez-vous que l'identifiant agrégé est initialisé au plus tard

J'obtiens l'erreur ci-dessous. Axon version 3.3.3

org.axonframework.eventsourcing.IncompatibleAggregateException: l'identificateur d'agrégat doit être non nul après l'application d'un événement. Assurez-vous que l'identificateur d'agrégat est initialisé au plus tard lors de la gestion de l'événement de création.

J'ai créé un UserAggregate. Il contient 2 événements:

  • UserCreated
  • UpdateUserEvent

Je suis capable de générer le premier événement (UserCreated) et il a été enregistré dans le magasin d'événements avec la séquence 0, mais lors de la génération du deuxième événement, j'ai eu l'erreur susmentionnée.

Des suggestions à ce sujet?

UserAggregate.java

@Aggregate
public class UserAggregate {

    @AggregateIdentifier
    private String id;


    private String email;
    private String password;

    public UserAggregate(String id, String email, String password) {
        super();
        this.id = id;
        this.email = email;
        this.password = password;
    }

    @CommandHandler
    public UserAggregate(CreateUser cmd) {
         AggregateLifecycle.apply(new UserCreated(cmd.getId(), cmd.getEmail(), cmd.getPassword()));
    }

    @CommandHandler
    public void handle(UpdateUserCmd cmd) {
         AggregateLifecycle.apply(new UpdateUserEvent(cmd.getId(), cmd.getEmail(),""));
    }

    @EventSourcingHandler
    public void userCreated(UserCreated event) {

        System.out.println("new User: email " + event.getEmail() +" Password: "+ event.getPassword());

        setId(event.getId());




    }

    @EventSourcingHandler
    public void updateUserEvent(UpdateUserEvent event) {

        System.out.println("new User: email " + event.getEmail());

        setId(event.getId());




    }


    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public UserAggregate() {
    }

}


0 commentaires

3 Réponses :


0
votes

Lorsque vous suivez le paradigme Event Sourcing pour vos agrégats, je suggère généralement que deux types de constructeurs soient présents dans le code:

  1. Un constructeur sans argument par défaut sans paramètres.
  2. Un (ou plusieurs) constructeur (s) qui gère la 'Commande de création d'agrégats'

Dans votre exemple, je vois un troisième constructeur pour définir l' id , l' email et le password . Je suppose que ce constructeur pourrait actuellement obstruer l'implémentation EventSourcedAggregate pour une validation correcte.

L'exception que vous recevez ne peut se produire que si le @AggregateIdentifier annoté @AggregateIdentifier n'est pas défini après que le gestionnaire de commande du constructeur (dans votre cas, UserAggregate(CreateUser) a terminé son unité de travail. Ainsi, en voyant votre code, ma seule intuition est cette "wild , constructeur inutilisé qui pourrait gêner les choses.

Enfin, je dois vous recommander d'utiliser une version plus récente d'Axon. La 3.3.3 est déjà assez éloignée de la version actuelle, étant la 4.2. De plus, aucun développement actif n'a lieu sur les versions d'Axon 3.x. Il est donc sage de mettre à jour la version, ce qui, je suppose, ne devrait pas être un gros problème car vous définissez toujours votre modèle de commande.


Mise à jour

Je viens de fermer le problème de Framework que vous avez ouvert. Axon fournit des moyens totalement différents de se connecter à l'envoi et à la gestion des messages, vous donnant des points d'interception plus propres que (Spring) AOP.

Si vous suivez les instructions suggérées pour utiliser un MessageDispatchInterceptor / MessageHandlerInterceptor ou l'option plus fine avec HandlerEnhancer , vous pouvez HandlerEnhancer à ces préoccupations transversales que vous recherchez.

En ce qui concerne la journalisation, le framework fournit même un LoggingInterceptor pour faire exactement ce dont vous avez besoin. Aucun AOP nécessaire.

J'espère que cela vous aidera Narasimha.


2 commentaires

Salut @Steven, le code AOP pose un problème. Pourriez-vous s'il vous plaît fournir une solution ou une solution de contournement pour ce problème? car j'ai besoin d'un code AOP pour lancer l'entrée de méthode et quitter les journaux automatiquement.


Mise à jour de la question et suppression du problème de cadre que vous avez créé en faveur de l'utilisation de la logique de distribution de messages / d'intercepteur de gestionnaire d'Axon. Cela fournit une poignée plus propre que AOP, donc je recommande fortement de suivre cette approche.



-1
votes

Merci @Steven pour la réponse.

Je suis capable de reproduire ce problème avec la version Axon 4.2 (dernière) également. Après avoir supprimé le code AOP ci-dessous dans mon projet, le problème a été résolu automatiquement. Il semble qu'Axon ne soit pas compatible avec la fonction AOP.

Code AOP:

2019-10-07 12:52:41.689  WARN 31736 --- [ault-executor-0] o.a.c.gateway.DefaultCommandGateway      : Command 'com.ms.axonspringboot.commands.UpdateUserCmd' resulted in org.axonframework.commandhandling.CommandExecutionException(Aggregate identifier must be non-null after applying an event. Make sure the aggregate identifier is initialized at the latest when handling the creation event.)
2019-10-07 12:52:41.710 ERROR 31736 --- [nio-7070-exec-3] o.a.c.c.C.[.[.[.[dispatcherServlet]      : Servlet.service() for servlet [dispatcherServlet] threw exception

org.axonframework.axonserver.connector.command.AxonServerRemoteCommandHandlingException: An exception was thrown by the remote message handling component: Aggregate identifier must be non-null after applying an event. Make sure the aggregate identifier is initialized at the latest when handling the creation event.
    at org.axonframework.axonserver.connector.ErrorCode.lambda$static$8(ErrorCode.java:84) ~[axon-server-connector-4.2.jar:4.2]
    at org.axonframework.axonserver.connector.ErrorCode.convert(ErrorCode.java:180) ~[axon-server-connector-4.2.jar:4.2]

Journaux d'erreurs de la version Axon 4.2

@Around("execution(* com.ms.axonspringboot..*(..))")
    public Object methodInvoke(ProceedingJoinPoint jointPoint) throws Throwable {
        LOGGER.debug(jointPoint.getSignature() + "::: Enters");
        Object obj = jointPoint.proceed();
        LOGGER.debug(jointPoint.getSignature() + "::: Exits");
        return obj;
    }


1 commentaires

Salut @Narasimha, j'ai mis à jour ma réponse originale et fourni des commentaires sur le problème Axon Framework que vous avez créé, selon lequel l'utilisation de (Spring) AOP pour des problèmes transversaux au sein de votre agrégat n'est pas l'approche la plus idéale à adopter. Il est préférable d'utiliser les intercepteurs Handler et Dispatch, ou pour des solutions plus fines, HandlerEnhancer (Definition) s. Cela vous dérangerait-il de reconsidérer le fait de marquer cela comme la bonne réponse? Du point de vue de l'utilisation d'Axon, cette réponse ne résout pas le problème que vous décrivez. Il s'agit plutôt d'une mise à jour de votre question initiale pour signaler le problème que vous rencontrez.



0
votes

J'apprends encore à connaître Axon, mais voici comment j'ai réussi à résoudre le problème. Fondamentalement, l'erreur indique que, lorsque UserAggregate est instancié, l'identificateur d'agrégat (également appelé clé primaire) ne doit pas être nul et doit avoir une valeur.

La séquence du cycle de vie est celle

  1. Il appelle un constructeur no args
  2. Il appelle le constructeur avec votre commande initiale, dans votre cas. À ce stade, votre identifiant global est toujours nul et nous attribuerons une valeur à l'étape suivante
  3. Il appelle ensuite un EventSourcingHandler qui gère l'événement que vous avez appliqué à l'étape précédente

En fonction des étapes ci-dessus, voici ce que vous devez faire:

  1. Créer un constructeur sans args
@Aggregate
public class UserAggregate {

    @AggregateIdentifier
    private String id;

    private String password;

    private String email;

    protected UserAggregate() {

    }

    @CommandHandler
    public UserAggregate(CreateUser cmd) {
        AggregateLifecycle.apply(
            new UserCreated(cmd.getId(),cmd.getEmail(),cmd.getPassword()));
    }

    @EventSourcingHandler
    public void on(UserCreated userCreated) {

        // this is where we instantiate the aggregate identifier
        this.id = userCreated.getId();

        //assign values to other fields
        this.email = userCreated.getEmail();
        this.password = userCreated.getPassword();

    }
}

  1. Créez un constructeur qui gère votre première commande:
@EventSourcingHandler
public void on(UserCreated userCreated) {

    // this is where we instantiate the aggregate identifier
    this.id = userCreated.getId();
    
    //assign values to other fields
    this.email = userCreated.getEmail();
    this.password = userCreated.getPassword();

}
  1. Enfin, ajoutez un gestionnaire de sourcing d'événements pour gérer l'événement UserCreated
@CommandHandler
public UserAggregate(CreateUser cmd) {
    AggregateLifecycle.apply(
         new UserCreated(cmd.getId(),cmd.getEmail(),cmd.getPassword()));
}

Et voici l'exemple complet:

protected UserAggregate() {

}


0 commentaires