ReflectionObjectConstructor implements ObjectConstructor<T> { @Override public T construct(Collection<DomainEvent> events) { if (events == null || events.isEmpty()) { return null; } DomainEvent firstEvent = events.stream().findFirst().get(); T instance = createInstance(firstEvent); LOGGER.debug("Created new {} with event {}", instance.getClass().getSimpleName(), firstEvent); events.stream() .skip(1) .forEach(p -> applyDomainEvent(instance, p)); invokePostConstructIfNeeded(instance); return instance; } ReflectionObjectConstructor(Class<T> targetClass); @Override T construct(Collection<DomainEvent> events); @Override T applyDomainEvents(T instance, Collection<DomainEvent> events);  }
@Test void whenGivenTwoEventsOutOfOrder_ShouldThrowException() { String identifier = UUID.randomUUID().toString(); DomainEvent event = new MyAggregateRootCreatedEvent(identifier, "Wallstreet"); DomainEvent secondEvent = new AddressUpdatedEvent(identifier, "streetName streetNumber"); ObjectConstructor<MyAggregateRoot> objectConstructor = new ReflectionObjectConstructor<>(MyAggregateRoot.class); Assertions.assertThrows(AggregateRootReconstructionException.class, () -> { MyAggregateRoot instance = objectConstructor.construct(Arrays.asList(secondEvent, event)); }); }
@Test void whenGivenEventThatHasNoConstructor_ShouldThrowException() { DomainEvent unMappedEvent = new UnMappedEvent(); ObjectConstructor<MyAggregateRoot> objectConstructor = new ReflectionObjectConstructor<>(MyAggregateRoot.class); Assertions.assertThrows(AggregateRootReconstructionException.class, () -> { MyAggregateRoot instance = objectConstructor.construct(Arrays.asList(unMappedEvent)); }); }
@Test void whenGivenEventThatHasNoMethod_ShouldThrowException() { DomainEvent event = new MyAggregateRootCreatedEvent(UUID.randomUUID().toString(), "Wallstreet"); DomainEvent unMappedEvent = new UnMappedEvent(); ObjectConstructor<MyAggregateRoot> objectConstructor = new ReflectionObjectConstructor<>(MyAggregateRoot.class); Assertions.assertThrows(AggregateRootReconstructionException.class, () -> { MyAggregateRoot instance = objectConstructor.construct(Arrays.asList(event, unMappedEvent)); }); }
@Test void whenGivenAnEventForAnObjectWhichCannotBeInstantiated_shouldThrowAnException() { DomainEvent event = new MyAggregateRootCreatedEvent(null, null); ObjectConstructor<NotInstantiatableAggregateRoot> objectConstructor = new ReflectionObjectConstructor<>(NotInstantiatableAggregateRoot.class); Assertions.assertThrows(AggregateRootReconstructionException.class, () -> { objectConstructor.construct(Arrays.asList(event)); }); }
@Test void whenGivenAnEventWhichCannotBeInvoked_shouldThrowAnException() { DomainEvent event = new MyAggregateRootCreatedEvent(UUID.randomUUID().toString(), "Wallstreet"); DomainEvent secondEvent = new AddressUpdatedEventWhichCannotBeInvoked(); ObjectConstructor<MyAggregateRoot> objectConstructor = new ReflectionObjectConstructor<>(MyAggregateRoot.class); Assertions.assertThrows(AggregateRootReconstructionException.class, () -> { objectConstructor.construct(Arrays.asList(event, secondEvent)); }); }
@Test void whenGivenAnEmptyListOfEvents_ShouldReturnNull() { ObjectConstructor<MyAggregateRoot> objectConstructor = new ReflectionObjectConstructor<>(MyAggregateRoot.class); MyAggregateRoot object = objectConstructor.construct(Collections.emptyList()); assertThat(object, nullValue()); }
@Test void whenGivenANullEventList_ShouldReturnNull() { ObjectConstructor<MyAggregateRoot> objectConstructor = new ReflectionObjectConstructor<>(MyAggregateRoot.class); MyAggregateRoot object = objectConstructor.construct(null); assertThat(object, nullValue()); }
@Test void whenGivenOneEvent_ShouldCreateInstance() { String sourceIdentifier = UUID.randomUUID().toString(); MyAggregateRootCreatedEvent event = new MyAggregateRootCreatedEvent(sourceIdentifier, "Wallstreet"); ObjectConstructor<MyAggregateRoot> objectConstructor = new ReflectionObjectConstructor<>(MyAggregateRoot.class); MyAggregateRoot instance = objectConstructor.construct(Arrays.asList(event)); assertThat(instance.identifier, equalTo(sourceIdentifier)); assertThat(instance.name, equalTo(event.getName())); assertThat(instance.address, nullValue()); assertThat(instance.postReconstructCalled, is(true)); assertThat(event.getId(), notNullValue()); assertThat(event.getAggregateRootIdentifier(), notNullValue()); assertThat(event.getTimestamp(), notNullValue()); }
@Test void whenGivenTwoEvents_ShouldCreateInstanceWithEventsReplayed() { String sourceIdentifier = UUID.randomUUID().toString(); MyAggregateRootCreatedEvent event = new MyAggregateRootCreatedEvent(sourceIdentifier, "Wallstreet"); AddressUpdatedEvent secondEvent = new AddressUpdatedEvent(sourceIdentifier, "streetName streetNumber"); ObjectConstructor<MyAggregateRoot> objectConstructor = new ReflectionObjectConstructor<>(MyAggregateRoot.class); MyAggregateRoot instance = objectConstructor.construct(Arrays.asList(event, secondEvent)); assertThat(instance.identifier, equalTo(sourceIdentifier)); assertThat(instance.name, equalTo(event.getName())); assertThat(instance.address, equalTo(secondEvent.getAddress())); assertThat(instance.postReconstructCalled, is(true)); }