Spring 值对象为@AggregateIdentifier和@TargetAggregateIdentifier

Spring 值对象为@AggregateIdentifier和@TargetAggregateIdentifier,spring,domain-driven-design,cqrs,event-sourcing,axon,Spring,Domain Driven Design,Cqrs,Event Sourcing,Axon,首先,我为这篇很长的文章道歉。有相当多的代码要显示有一个问题的详细理解,因此失去了东西张贴。。。请您阅读全文:-) 我正在尝试使用Axon框架和Spring引导应用程序开发一个基于事件源的应用程序。我在下面提供了许多类定义,它们构成了聚合、命令和事件实现 我编写了一个非常简单的测试应用程序(Spring),它只向Axon发送一个CreateAppointmentCommand。此create命令使用手动分配的AppointmentId(它是AbstractId的子类)并返回此ID供以后使用。这没

首先,我为这篇很长的文章道歉。有相当多的代码要显示有一个问题的详细理解,因此失去了东西张贴。。。请您阅读全文:-)

我正在尝试使用Axon框架和Spring引导应用程序开发一个基于事件源的应用程序。我在下面提供了许多类定义,它们构成了聚合、命令和事件实现

我编写了一个非常简单的测试应用程序(Spring),它只向Axon发送一个CreateAppointmentCommand。此create命令使用手动分配的AppointmentId(它是AbstractId的子类)并返回此ID供以后使用。这没什么错,Appointment中的构造函数命令处理程序按预期调用,相应的ApppointmentCreatedEvent被激发,Appointment类也按预期处理。到目前为止,一切顺利。 当我使用create命令返回的ID发送ConfirmAppointmentCommand时,问题就出现了。在这些情况下,我会收到一条错误消息:

命令“confirMapPointCommand”导致org.axonframework.commandhandling.CommandExecutionException(为类约会提供的id类型错误。应为:类约会id,获取类java.lang.String)

我不了解此设置中与此错误消息相关的一些内容:

  • 为什么create命令和事件在使用与confirm命令/事件相同的方法(至少到目前为止我的理解是这样)时会按预期工作
  • 为什么Axon抱怨AppointmentId作为(可能是聚合)的标识符,而相应的代码(见下文)同时为@AggregateIdentier和@TargetAggregateIdentiter注释两种字符串类型
  • Axon在使用聚合和实体(在本例中是由Spring管理并链接到关系数据库的JPA存储库)时,是否允许使用相同的代码将聚合直接存储到持久存储(我不认为我应该使用参考指南中描述的状态存储聚合方法,因为我仍然希望我的解决方案是事件驱动的,用于创建和更新约会)
  • 这是使用事件机制保持聚合状态最新的正确方法吗?是否可以使用另一个Spring@Component类实现一系列@EventHandler方法来对关系数据库执行CRUD操作。在后者中,创建事件按照约会的预期进行处理获取存储在数据库中。由于前面的错误消息,未触发确认事件
  • 关于第4项,我有点困惑,如果Axon重新启动并开始向第4项中的事件处理程序发送不同的事件,会发生什么。这会不会导致很多数据库错误,因为约会仍然在数据库中,或者在最坏的情况下,相同约会的无休止重复我在本项目中使用的方法以及我对事件驱动应用程序/服务的理解似乎有问题
  • 请查看下面不同的类定义以了解更多详细信息。首先,我有根聚合约会,它将同时用作JPA实体

    @Aggregate
    @Entity
    @Table(name = "t_appointment")
    public final class Appointment extends AbstractEntity<AppointmentId> {
    
        //JPA annotated class members left out for brevity
    
        @PersistenceConstructor
        private Appointment() {
            super(null);
            //Sets all remaining class members to null.
        }
    
        @CommandHandler
        private Appointment(CreateAppointmentCommand command) {
            super(command.getAggregateId());
            validateFields(getEntityId(), ...);
            AggregateLifecycle.apply(new AppointmentCreatedEvent(getEntityId(), ...);
        }
    
        @EventSourcingHandler
        private void on(AppointmentCreatedEvent event) {
            validateFields(event.getAggregateId(), ...);
            initFields(event.getAggregateId(), ...);
        }
    
        private void validateFields(AppointmentId appointmentId, ...) {
            //Check if all arguments are within the required boundaries.
        }
    
        private void initFields(AppointmentId appointmentId, ...) {
            //Set all class level variables to passed in value.
        }
    
        @CommandHandler
        private void handle(ConfirmAppointmentCommand command) {
            AggregateLifecycle.apply(new AppointmentConfirmedEvent(command.getAggregateId()));
        }
    
        @EventSourcingHandler
        private void on(AppointmentConfirmedEvent event) {
            confirm();
        }
    
        public void confirm() {
            changeState(State.CONFIRMED);
        }   
    
        //Similar state changing command/event handlers left out for brevity.
    
        private void changeState(State newState) {
            switch (state) {
            ...
            }
        }
    
        //All getter methods left out for brevity. The aggregate does NOT provide any setters.
    
        @Override
        public String toString() {
            return "Appointment [...]";
        }
    }
    
    在聚合中,使用了许多命令和事件。每个命令都是command的子类

    @SuppressWarnings("serial")
    public abstract class Command<AGGREGATE_ID extends AbstractId> implements Serializable{
    
        private AGGREGATE_ID aggregateId;
    
        @TargetAggregateIdentifier
        private String targetId;
    
        protected Command(AGGREGATE_ID aggregateId) {
            if(aggregateId == null) {
                throw new InvalidArgumentException(...);
            }   
            this.aggregateId = aggregateId;
            this.targetId = aggregateId != null ? aggregateId.getId() : null;
        }
    
        public final AGGREGATE_ID getAggregateId() {
            return aggregateId;
        }   
    }
    
    @SuppressWarnings(“串行”)
    公共抽象类命令实现可序列化{
    私有聚合_idaggregateId;
    @目标聚集标识符
    私有字符串targetId;
    受保护命令(聚合ID聚合ID){
    if(aggregateId==null){
    抛出新的InvalidArgumentException(…);
    }   
    this.aggregateId=aggregateId;
    this.targetId=aggregateId!=null?aggregateId.getId():null;
    }
    公共最终聚合\u ID getAggregateId(){
    返回集合ID;
    }   
    }
    
    一个指定的命令类(这在我的方法中造成了困难)是confirmappointCommand,它实际上只是基本命令类的一个具体实现,因此实现非常简单

    public final class ConfirmAppointmentCommand extends Command<AppointmentId> {
        private static final long serialVersionUID = 6618106729289153342L;
    
        public ConfirmAppointmentCommand(AppointmentId appointmentId) {
            super(appointmentId);       
        }   
    }
    
    public final class AppointmentCreatedEvent extends DomainEvent<AppointmentId> {
        private static final long serialVersionUID = -5265970306200850734L;
    
        //Class members left out for brevity
    
        public AppointmentCreatedEvent(AppointmentId appointmentId, ...) {
            super(appointmentId);
    
            //Check to verify the provided method arguments are left out.
    
            //Set all verified class members to the corresponding values.
        }
    
        //Getters for all class members, no setters are being implemented.
    }
    
    public final类confirmappointCommand扩展命令{
    私有静态最终长serialVersionUID=6618106729289153342L;
    公共确认点命令(任命ID任命ID){
    超级(任命ID);
    }   
    }
    
    CreateAppointmentCommand与ConfirmAppointCommand非常相似,定义如下

    public final class CreateAppointmentCommand extends Command<AppointmentId> {
        private static final long serialVersionUID = -5445719522854349344L;
    
        //Some additional class members left out for brevity.
    
        public CreateAppointmentCommand(AppointmentId appointmentId, ...) {
            super(appointmentId);
    
            //Check to verify the provided method arguments are left out.
    
            //Set all verified class members to the corresponding values.
        }
    
        //Getters for all class members, no setters are being implemented.
    
    }
    
    public final类createAppointCommand扩展命令{
    私有静态最终长serialVersionUID=-5445719522854349344L;
    //为了简洁起见,遗漏了一些额外的类成员。
    公共CreateAppointmentCommand(AppointmentId AppointmentId,…){
    超级(任命ID);
    //检查以验证是否遗漏了提供的方法参数。
    //将所有已验证的类成员设置为相应的值。
    }
    //对于所有类成员,没有实现任何setter。
    }
    
    对于项目中使用的不同事件,使用了类似的方法。所有事件都是下面定义的基本DomainEvent类的子类

        @SuppressWarnings("serial")
        public abstract class DomainEvent<T extends AbstractId> implements Serializable{
    
            private T aggregateId;
    
    
            protected DomainEvent(T aggregateId) {
                if(aggregateId == null) {
                    throw new InvalidArgumentException(ErrorCodes.AGGREGATE_ID_MISSING);
                }           
                this.aggregateId = aggregateId;
            }
    
            public final T getAggregateId() {
                return aggregateId;
            }   
        }
    
    @SuppressWarnings(“串行”)
    公共抽象类DomainEvent实现可序列化{
    私人T-aggregateId;
    受保护域事件(T aggregateId){
    if(aggregateId==null){
    抛出新的InvalidArgumentException(ErrorCodes.AGGREGATE\u ID\u MISSING);
    }           
    this.aggregateId=aggregateId;
    }
    公共最终T getAggregateId(){
    返回集合ID;
    }   
    }
    
    任命CreateDevent相当直截了当

    public final class ConfirmAppointmentCommand extends Command<AppointmentId> {
        private static final long serialVersionUID = 6618106729289153342L;
    
        public ConfirmAppointmentCommand(AppointmentId appointmentId) {
            super(appointmentId);       
        }   
    }
    
    public final class AppointmentCreatedEvent extends DomainEvent<AppointmentId> {
        private static final long serialVersionUID = -5265970306200850734L;
    
        //Class members left out for brevity
    
        public AppointmentCreatedEvent(AppointmentId appointmentId, ...) {
            super(appointmentId);
    
            //Check to verify the provided method arguments are left out.
    
            //Set all verified class members to the corresponding values.
        }
    
        //Getters for all class members, no setters are being implemented.
    }
    
    public final class AppointmentCreatedEvent
    
    public final class AppointmentConfirmedEvent extends DomainEvent<AppointmentId> {
        private static final long serialVersionUID = 5415394808454635999L;
    
        public AppointmentConfirmedEvent(AppointmentId appointmentId) {
            super(appointmentId);       
        }
    }