问题
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