@Override default Optional<Long> lastSequenceNumberFor(String aggregateIdentifier) { return readEvents(aggregateIdentifier).asStream().map(DomainEventMessage::getSequenceNumber) .max(Long::compareTo); } }
private List<DomainEventMessage<?>> unexpectedEvents() { if (events == null) { if (expectedVersion >= actualVersion) { return Collections.emptyList(); } events = eventStore.readEvents(aggregateIdentifier, expectedVersion + 1).asStream() .filter(event -> event.getSequenceNumber() <= actualVersion).collect(toList()); } return events; }
/** * Returns the last known sequence number for the given {@code aggregateIdentifier}. * <p> * While it's recommended to use the sequence numbers from the {@link DomainEventStream}, there are cases where * knowing the sequence number is required, without having read the actual events. In such case, this method is a * viable alternative. * * @param aggregateIdentifier The identifier to find the last sequence number for * @return an optional with the highest sequence number, or an empty optional if the aggregate identifier wasn't * found */ default Optional<Long> lastSequenceNumberFor(String aggregateIdentifier) { return readEvents(aggregateIdentifier).asStream().map(DomainEventMessage::getSequenceNumber).max(Long::compareTo); }
/** * Open an event stream containing all domain events belonging to the given {@code aggregateIdentifier}. * <p> * The returned stream is <em>finite</em>, ending with the last known event of the aggregate. If the event store * holds no events of the given aggregate an empty stream is returned. * <p> * The default implementation invokes {@link #readEvents(String)} and then filters out events with a sequence number * smaller than {@code firstSequenceNumber}. * * @param aggregateIdentifier the identifier of the aggregate whose events to fetch * @param firstSequenceNumber the expected sequence number of the first event in the returned stream * @return a stream of all currently stored events of the aggregate */ default DomainEventStream readEvents(String aggregateIdentifier, long firstSequenceNumber) { DomainEventStream wholeStream = readEvents(aggregateIdentifier); return DomainEventStream .of(wholeStream.asStream().filter(event -> event.getSequenceNumber() >= firstSequenceNumber), wholeStream::getLastSequenceNumber); }
@Test public void testLoadNonExistent() { assertEquals(0L, testSubject.readEvents(randomUUID().toString()).asStream().count()); }
@Test @SuppressWarnings("OptionalGetWithoutIsPresent") public void testLoad_LargeAmountOfEvents() { int eventCount = testSubject.batchSize() + 10; testSubject.appendEvents(createEvents(eventCount)); assertEquals(eventCount, testSubject.readEvents(AGGREGATE).asStream().count()); assertEquals(eventCount - 1, testSubject.readEvents(AGGREGATE).asStream().reduce((a, b) -> b).get().getSequenceNumber()); }
@Test @SuppressWarnings("OptionalGetWithoutIsPresent") public void testReadPartialStream() { testSubject.appendEvents(createEvents(5)); assertEquals(2L, testSubject.readEvents(AGGREGATE, 2).asStream().findFirst().get().getSequenceNumber()); assertEquals(4L, testSubject.readEvents(AGGREGATE, 2).asStream().reduce((a, b) -> b).get().getSequenceNumber()); assertEquals(3L, testSubject.readEvents(AGGREGATE, 2).asStream().count()); }
@Test public void testStoreAndLoadEvents() { testSubject.appendEvents(createEvents(4)); assertEquals(4, testSubject.readEvents(AGGREGATE).asStream().count()); testSubject.appendEvents(createEvent("otherAggregate", 0)); assertEquals(4, testSubject.readEvents(AGGREGATE).asStream().count()); assertEquals(1, testSubject.readEvents("otherAggregate").asStream().count()); }
@Test public void orderInEventStore() { configuration.commandGateway().sendAndWait(command); assertEquals(expectedDescriptions(command), configuration.eventStore() .readEvents(aggregateIdentifier) .asStream() .map(Message::getPayload) .map(MyEvent.class::cast) .map(MyEvent::getDescription) .collect(Collectors.toList())); }
@Test public void testStoreAndLoadEventsArray() { testSubject.appendEvents(createEvent(0), createEvent(1)); assertEquals(2, testSubject.readEvents(AGGREGATE).asStream().count()); }
@Test @DirtiesContext @SuppressWarnings({"unchecked", "OptionalGetWithoutIsPresent"}) public void testStoreAndLoadEventsWithUpcaster() { EventUpcaster mockUpcasterChain = mock(EventUpcaster.class); when(mockUpcasterChain.upcast(isA(Stream.class))).thenAnswer(invocation -> { Stream<?> inputStream = (Stream) invocation.getArguments()[0]; return inputStream.flatMap(e -> Stream.of(e, e)); }); testSubject = createEngine(mockUpcasterChain); testSubject.appendEvents(createEvents(4)); List<DomainEventMessage> upcastedEvents = testSubject.readEvents(AGGREGATE).asStream().collect(toList()); assertEquals(8, upcastedEvents.size()); Iterator<DomainEventMessage> iterator = upcastedEvents.iterator(); while (iterator.hasNext()) { DomainEventMessage event1 = iterator.next(), event2 = iterator.next(); assertEquals(event1.getAggregateIdentifier(), event2.getAggregateIdentifier()); assertEquals(event1.getSequenceNumber(), event2.getSequenceNumber()); assertEquals(event1.getPayload(), event2.getPayload()); assertEquals(event1.getMetaData(), event2.getMetaData()); } }
@Override default Optional<Long> lastSequenceNumberFor(String aggregateIdentifier) { return readEvents(aggregateIdentifier).asStream().map(DomainEventMessage::getSequenceNumber) .max(Long::compareTo); } }
/** * Returns the last known sequence number for the given {@code aggregateIdentifier}. * <p> * While it's recommended to use the sequence numbers from the {@link DomainEventStream}, there are cases where * knowing the sequence number is required, without having read the actual events. In such case, this method is a * viable alternative. * * @param aggregateIdentifier The identifier to find the last sequence number for * @return an optional with the highest sequence number, or an empty optional if the aggregate identifier wasn't * found */ default Optional<Long> lastSequenceNumberFor(String aggregateIdentifier) { return readEvents(aggregateIdentifier).asStream().map(DomainEventMessage::getSequenceNumber).max(Long::compareTo); }
/** * Returns the last known sequence number for the given {@code aggregateIdentifier}. * <p> * While it's recommended to use the sequence numbers from the {@link DomainEventStream}, there are cases where * knowing the sequence number is required, without having read the actual events. In such case, this method is a * viable alternative. * * @param aggregateIdentifier The identifier to find the last sequence number for * @return an optional with the highest sequence number, or an empty optional if the aggregate identifier wasn't * found */ default Optional<Long> lastSequenceNumberFor(String aggregateIdentifier) { return readEvents(aggregateIdentifier).asStream().map(DomainEventMessage::getSequenceNumber).max(Long::compareTo); }
/** * Returns the last known sequence number of an Event for the given {@code aggregateIdentifier}. * <p> * It is preferred to retrieve the last known sequence number from the Domain Event Stream when sourcing an * Aggregate from events. However, this method provides an alternative in cases no events have been read. For * example when using state storage. * * @param aggregateIdentifier The identifier of the aggregate to find the highest sequence for * @return An optional containing the highest sequence number found, or an empty optional is no events are found * for this aggregate */ default Optional<Long> lastSequenceNumberFor(String aggregateIdentifier) { return readEvents(aggregateIdentifier).asStream().map(DomainEventMessage::getSequenceNumber).max(Long::compareTo); } }
private List<DomainEventMessage<?>> unexpectedEvents() { if (events == null) { if (expectedVersion >= actualVersion) { return Collections.emptyList(); } events = eventStore.readEvents(aggregateIdentifier, expectedVersion + 1).asStream() .filter(event -> event.getSequenceNumber() <= actualVersion).collect(toList()); } return events; }
private List<DomainEventMessage<?>> unexpectedEvents() { if (events == null) { if (expectedVersion >= actualVersion) { return Collections.emptyList(); } events = eventStore.readEvents(aggregateIdentifier, expectedVersion + 1).asStream() .filter(event -> event.getSequenceNumber() <= actualVersion).collect(toList()); } return events; }
/** * Open an event stream containing all domain events belonging to the given {@code aggregateIdentifier}. * <p> * The returned stream is <em>finite</em>, ending with the last known event of the aggregate. If the event store * holds no events of the given aggregate an empty stream is returned. * <p> * The default implementation invokes {@link #readEvents(String)} and then filters out events with a sequence number * smaller than {@code firstSequenceNumber}. * * @param aggregateIdentifier the identifier of the aggregate whose events to fetch * @param firstSequenceNumber the expected sequence number of the first event in the returned stream * @return a stream of all currently stored events of the aggregate */ default DomainEventStream readEvents(String aggregateIdentifier, long firstSequenceNumber) { DomainEventStream wholeStream = readEvents(aggregateIdentifier); return DomainEventStream .of(wholeStream.asStream().filter(event -> event.getSequenceNumber() >= firstSequenceNumber), wholeStream::getLastSequenceNumber); }
/** * Open an event stream containing all domain events belonging to the given {@code aggregateIdentifier}. * <p> * The returned stream is <em>finite</em>, ending with the last known event of the aggregate. If the event store * holds no events of the given aggregate an empty stream is returned. * <p> * The default implementation invokes {@link #readEvents(String)} and then filters out events with a sequence number * smaller than {@code firstSequenceNumber}. * * @param aggregateIdentifier the identifier of the aggregate whose events to fetch * @param firstSequenceNumber the expected sequence number of the first event in the returned stream * @return a stream of all currently stored events of the aggregate */ default DomainEventStream readEvents(String aggregateIdentifier, long firstSequenceNumber) { DomainEventStream wholeStream = readEvents(aggregateIdentifier); return DomainEventStream .of(wholeStream.asStream().filter(event -> event.getSequenceNumber() >= firstSequenceNumber), wholeStream::getLastSequenceNumber); }