Can it register an domain event in the constructor of aggregate root, when using Spring Data Common

大憨熊 提交于 2019-12-13 03:15:46

问题


The aggregate will be created by some application service, not by another aggregate.

Like this

SomeAggregate aggregate = new SomeAggregate();
repo.save(aggregate);

The expectation is that the aggregate is saved and one SomeAggregateCreated event is published when the application service is over. I have tested it, it is not always effective, sometimes the event is not registered immediately after the constructor is executed.

This is the teacher class:

public class Teacher extends AbstractAggregateRoot<Teacher> {

    public Teacher() {
        registerEvent(new TeacherAdded(id, name));
    }
}

This is the TeacherAdded:

@AllArgsConstructor
@Getter
@ToString
@EqualsAndHashCode(callSuper = true)
public class TeacherAdded extends AbstractDomainEvent {
    private TeacherId teacherId;    
    private String name;
}

This is AbstractDomainEvent and DomainEvent

@Getter
@ToString()
@EqualsAndHashCode()
public abstract class AbstractDomainEvent implements DomainEvent {
    protected Date occurredOn;

    public AbstractDomainEvent() {
        this(new Date());
    }

    public AbstractDomainEvent(Date occurredOn) {
        this.occurredOn = occurredOn != null ? occurredOn : new Date();
    }

    @Override
    public Date occurredOn() {
        return occurredOn;
    }    
}

public interface DomainEvent {
    public Date occurredOn();
}

The AbstractAggregateRoot is copied from org.springframework.data.domain.AbstractAggregateRoot<A>, and the hasCapturedEvent method is added for testing.

public boolean hasCapturedEvent(DomainEvent event) {
    return domainEvents.contains(event);
}

If I run this:

// ...
TeacherAdded teacherAdded = new TeacherAdded(teacherId, teacherName);
Teacher teacher = new Teacher();
assertTrue(teacher.hasCapturedEvent(teacherAdded)); 

It sometimes fails and sometimes succeeds.


回答1:


The answer is yes. The domain events registered by the constructor can be published and listened normally. They are the same as the events registered by the usual method.

The test I provided in the problem description is defective. After modifying AbstractDomainEvent.hasCapturedEvent and its associated code, the test can pass.

This is new AbstractDomainEvent.hasCapturedEvent and test method.

    public boolean hasCapturedEvent(DomainEvent event, long occurredOnAdjustment) {
        if (occurredOnAdjustment <= 0) {
            return this.domainEvents.contains(event);
        } else {
            return this.domainEvents.stream().anyMatch(eventOnStream -> {
                return eventOnStream.equalsExcludedOccurTime(event)
                    && System.currentTimeMillis() - eventOnStream.occurredOn().getTime() <= occurredOnAdjustment;
            });
        }
    }

        TeacherAdded teacherAdded = new TeacherAdded(teacherId, teacherName);
        Teacher teacher = new Teacher();
        assertTrue(teacher.hasCapturedEvent(teacherAdded, 1000L)); 

This is new TeacherAdded.

public interface DomainEvent {
    public Date occurredOn();

    public default boolean equalsExcludedOccurTime(DomainEvent other) {
        return false;
    }
}

@lombok...
public class TeacherAdded extends AbstractDomainEvent {
    private TeacherId teacherId;    
    private String name;

    @Override
    public boolean equalsExcludedOccurTime(DomainEvent other) {
        if (!(other instanceof TeacherAdded)) {
            return false;
        } else {
            TeacherAdded other2 = (TeacherAdded)other;
            return Objects.equals(teacherId, other2.teacherId)
                && Objects.equals(name, other2.name);
        }
    }
}


来源:https://stackoverflow.com/questions/52073819/can-it-register-an-domain-event-in-the-constructor-of-aggregate-root-when-using

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!