/** * Reads the events for the given aggregateIdentifier from the eventStore. this method may be overridden to * add pre or postprocessing to the loading of an event stream * * @param aggregateIdentifier the identifier of the aggregate to load * @return the domain event stream for the given aggregateIdentifier */ protected DomainEventStream readEvents(String aggregateIdentifier) { return eventStore.readEvents(aggregateIdentifier); }
@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; }
/** * 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); }
@Override public void run() { DomainEventStream eventStream = eventStore.readEvents(identifier); // a snapshot should only be stored if the snapshot replaces at least more than one event long firstEventSequenceNumber = eventStream.peek().getSequenceNumber(); DomainEventMessage snapshotEvent = createSnapshot(aggregateType, identifier, eventStream); if (snapshotEvent != null && snapshotEvent.getSequenceNumber() > firstEventSequenceNumber) { eventStore.storeSnapshot(snapshotEvent); } } }
@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 testPublicationOrderIsMaintained_AggregateAdded() { String aggregateId = UUID.randomUUID().toString(); GenericDomainEventMessage<StubAggregateCreatedEvent> event = new GenericDomainEventMessage<>("test", aggregateId, 0, new StubAggregateCreatedEvent(aggregateId)); when(eventStore.readEvents(aggregateId)).thenReturn(DomainEventStream.of(event)); doAnswer(invocation -> { System.out.println("Published event: " + invocation.getArguments()[0].toString()); return Void.class; }).when(eventStore).publish(isA(EventMessage.class)); commandBus.dispatch(asCommandMessage(new UpdateStubAggregateWithExtraEventCommand(aggregateId))); InOrder inOrder = inOrder(eventStore, eventStore, eventStore); inOrder.verify(eventStore).publish(isA(DomainEventMessage.class)); inOrder.verify(eventStore).publish(argThat(new NotADomainEventMatcher())); inOrder.verify(eventStore).publish(isA(DomainEventMessage.class)); }
logger.debug("Aggregate {} not in first level cache, loading fresh one from Event Store", aggregateIdentifier); DomainEventStream eventStream = eventStore.readEvents(aggregateIdentifier); SnapshotTrigger trigger = snapshotTriggerDefinition.prepareTrigger(aggregateFactory.getAggregateType()); if (!eventStream.hasNext()) {
DomainEventStream storedEvents = eventStore.readEvents(aggregateIdentifier); assertTrue(storedEvents.hasNext()); while (storedEvents.hasNext()) {
DomainEventStream storedEvents = eventStore.readEvents(aggregateIdentifier); assertTrue(storedEvents.hasNext());
/** * Reads the events for the given aggregateIdentifier from the eventStore. this method may be overridden to * add pre or postprocessing to the loading of an event stream * * @param aggregateIdentifier the identifier of the aggregate to load * @return the domain event stream for the given aggregateIdentifier */ protected DomainEventStream readEvents(String aggregateIdentifier) { return eventStore.readEvents(aggregateIdentifier); }
/** * Reads the events for the given aggregateIdentifier from the eventStore. this method may be overridden to * add pre or postprocessing to the loading of an event stream * * @param aggregateIdentifier the identifier of the aggregate to load * @return the domain event stream for the given aggregateIdentifier */ protected DomainEventStream readEvents(String aggregateIdentifier) { return eventStore.readEvents(aggregateIdentifier); }
@Override 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); }
@Override public void run() { DomainEventStream eventStream = eventStore.readEvents(identifier); // a snapshot should only be stored if the snapshot replaces at least more than one event long firstEventSequenceNumber = eventStream.peek().getSequenceNumber(); DomainEventMessage snapshotEvent = createSnapshot(aggregateType, identifier, eventStream); if (snapshotEvent != null && snapshotEvent.getSequenceNumber() > firstEventSequenceNumber) { eventStore.storeSnapshot(snapshotEvent); } } }
@Override public void run() { DomainEventStream eventStream = eventStore.readEvents(identifier); // a snapshot should only be stored if the snapshot replaces at least more than one event long firstEventSequenceNumber = eventStream.peek().getSequenceNumber(); DomainEventMessage snapshotEvent = createSnapshot(aggregateType, identifier, eventStream); if (snapshotEvent != null && snapshotEvent.getSequenceNumber() > firstEventSequenceNumber) { eventStore.storeSnapshot(snapshotEvent); } } }