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:
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?
@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() { } }
3 Réponses :
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:
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.
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.
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; }
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.
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
EventSourcingHandler
qui gère l'événement que vous avez appliqué à l'étape précédenteEn fonction des étapes ci-dessus, voici ce que vous devez faire:
@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(); } }
@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(); }
UserCreated
@CommandHandler public UserAggregate(CreateUser cmd) { AggregateLifecycle.apply( new UserCreated(cmd.getId(),cmd.getEmail(),cmd.getPassword())); }
Et voici l'exemple complet:
protected UserAggregate() { }