@Override public Object getSequenceIdentifierFor(EventMessage event) { if (event instanceof DomainEventMessage) { return ((DomainEventMessage) event).getAggregateIdentifier(); } return null; } }
@Override public String resolveParameterValue(Message message) { if (message instanceof DomainEventMessage) { return ((DomainEventMessage) message).getAggregateIdentifier(); } throw new IllegalArgumentException(); }
@Override public void storeSnapshot(DomainEventMessage<?> snapshot) { snapshots.compute(snapshot.getAggregateIdentifier(), (aggregateId, snapshotsSoFar) -> { if (snapshotsSoFar == null) { CopyOnWriteArrayList<DomainEventMessage<?>> newSnapshots = new CopyOnWriteArrayList<>(); newSnapshots.add(snapshot); return newSnapshots; } snapshotsSoFar.add(snapshot); return snapshotsSoFar; }); }
/** * Returns a Stream of all DomainEventMessages that have been staged for publication by an Aggregate with given * {@code aggregateIdentifier}. * * @param aggregateIdentifier The identifier of the aggregate to get staged events for * @return a Stream of DomainEventMessage of the identified aggregate */ protected Stream<? extends DomainEventMessage<?>> stagedDomainEventMessages(String aggregateIdentifier) { return queuedMessages().stream() .filter(m -> m instanceof DomainEventMessage) .map(m -> (DomainEventMessage<?>) m) .filter(m -> aggregateIdentifier.equals(m.getAggregateIdentifier())); }
protected void scheduleSnapshot(DomainEventMessage msg) { snapshotter.scheduleSnapshot(aggregateType, msg.getAggregateIdentifier()); counter = 0; }
@Override public DomainEventStream readEvents(String aggregateIdentifier, long firstSequenceNumber) { AtomicReference<Long> sequenceNumber = new AtomicReference<>(); Stream<? extends DomainEventMessage<?>> stream = events.values().stream().filter(event -> event instanceof DomainEventMessage<?>) .map(event -> (DomainEventMessage<?>) event) .filter(event -> aggregateIdentifier.equals(event.getAggregateIdentifier()) && event.getSequenceNumber() >= firstSequenceNumber) .peek(event -> sequenceNumber.set(event.getSequenceNumber())); return DomainEventStream.of(stream, sequenceNumber::get); }
/** * Construct a new event entry from a published domain event message to enable storing the event or sending it to a * remote location. * <p> * The given {@code serializer} will be used to serialize the payload and metadata in the given {@code * eventMessage}. The type of the serialized data will be the same as the given {@code contentType}. * * @param eventMessage The event message to convert to a serialized event entry * @param serializer The serializer to convert the event * @param contentType The data type of the payload and metadata after serialization */ public AbstractSnapshotEventEntry(DomainEventMessage<?> eventMessage, Serializer serializer, Class<T> contentType) { super(eventMessage, serializer, contentType); type = eventMessage.getType(); aggregateIdentifier = eventMessage.getAggregateIdentifier(); sequenceNumber = eventMessage.getSequenceNumber(); }
/** * Construct a new event entry from a published domain event message to enable storing the event or sending it to a * remote location. * <p> * The given {@code serializer} will be used to serialize the payload and metadata in the given {@code eventMessage}. * The type of the serialized data will be the same as the given {@code contentType}. * * @param eventMessage The event message to convert to a serialized event entry * @param serializer The serializer to convert the event * @param contentType The data type of the payload and metadata after serialization */ public AbstractDomainEventEntry(DomainEventMessage<?> eventMessage, Serializer serializer, Class<T> contentType) { super(eventMessage, serializer, contentType); type = eventMessage.getType(); aggregateIdentifier = eventMessage.getAggregateIdentifier(); sequenceNumber = eventMessage.getSequenceNumber(); }
/** * Initialize a DomainEventMessage originating from an aggregate. * * @param trackingToken Tracking token of the event * @param delegate Delegate domain event containing other event data */ public GenericTrackedDomainEventMessage(TrackingToken trackingToken, DomainEventMessage<T> delegate) { this(trackingToken, delegate.getType(), delegate.getAggregateIdentifier(), delegate.getSequenceNumber(), delegate, delegate.getTimestamp()); }
@EventHandler protected void handle(EventMessage event) { identifier = ((DomainEventMessage) event).getAggregateIdentifier(); }
@Override public <T> Message<T> convertToOutboundMessage(EventMessage<T> event) { Map<String, Object> headers = new HashMap<>(); event.getMetaData().forEach(headers::put); headers.put(MESSAGE_ID, event.getIdentifier()); if (event instanceof DomainEventMessage) { headers.put(AGGREGATE_ID, ((DomainEventMessage) event).getAggregateIdentifier()); headers.put(AGGREGATE_SEQ, ((DomainEventMessage) event).getSequenceNumber()); headers.put(AGGREGATE_TYPE, ((DomainEventMessage) event).getType()); } return new GenericMessage<>(event.getPayload(), new SettableTimestampMessageHeaders(headers, event.getTimestamp().toEpochMilli())); }
@Override protected void storeSnapshot(DomainEventMessage<?> snapshot, Serializer serializer) { try { entityManager().merge(createSnapshotEntity(snapshot, serializer)); deleteSnapshots(snapshot.getAggregateIdentifier(), snapshot.getSequenceNumber()); if (explicitFlush) { entityManager().flush(); } } catch (Exception e) { handlePersistenceException(e, snapshot); } }
/** * Invoke when an Exception is raised while persisting an Event or Snapshot. * * @param exception The exception raised while persisting an Event * @param failedEvent The EventMessage that could not be persisted */ protected void handlePersistenceException(Exception exception, EventMessage<?> failedEvent) { String eventDescription; if (failedEvent instanceof DomainEventMessage<?>) { DomainEventMessage<?> failedDomainEvent = (DomainEventMessage<?>) failedEvent; eventDescription = format("An event for aggregate [%s] at sequence [%d]", failedDomainEvent.getAggregateIdentifier(), failedDomainEvent.getSequenceNumber()); } else { eventDescription = format("An event with identifier [%s]", failedEvent.getIdentifier()); } if (persistenceExceptionResolver != null && persistenceExceptionResolver.isDuplicateKeyViolation(exception)) { throw new ConcurrencyException(eventDescription + " was already inserted", exception); } else { throw new EventStoreException(eventDescription + " could not be persisted", exception); } }
/** * Generate defaults headers to recognise an event message. * * @param message event message. * @param serializedObject payload. * @return headers */ public static Map<String, Object> defaultHeaders(EventMessage<?> message, SerializedObject<?> serializedObject) { Assert.notNull(message, () -> "Event message cannot be null"); Assert.notNull(serializedObject, () -> "Serialized Object cannot be null"); Assert.notNull(serializedObject.getType(), () -> "SerializedObject Type cannot be null"); HashMap<String, Object> headers = new HashMap<>(); headers.put(MESSAGE_ID, message.getIdentifier()); headers.put(MESSAGE_TYPE, serializedObject.getType().getName()); headers.put(MESSAGE_REVISION, serializedObject.getType().getRevision()); headers.put(MESSAGE_TIMESTAMP, message.getTimestamp()); if (message instanceof DomainEventMessage) { headers.put(AGGREGATE_ID, ((DomainEventMessage<?>) message).getAggregateIdentifier()); headers.put(AGGREGATE_SEQ, ((DomainEventMessage<?>) message).getSequenceNumber()); headers.put(AGGREGATE_TYPE, ((DomainEventMessage<?>) message).getType()); } return Collections.unmodifiableMap(headers); }
SerializedObject<?> metaData = event.serializeMetaData(serializer, dataType); preparedStatement.setString(1, event.getIdentifier()); preparedStatement.setString(2, event.getAggregateIdentifier()); preparedStatement.setLong(3, event.getSequenceNumber()); preparedStatement.setString(4, event.getType());
preparedStatement.setString(2, snapshot.getAggregateIdentifier()); preparedStatement.setLong(3, snapshot.getSequenceNumber()); preparedStatement.setString(4, snapshot.getType());
@Override protected void storeSnapshot(DomainEventMessage<?> snapshot, Serializer serializer) { transactionManager.executeInTransaction(() -> { try { executeUpdates( getConnection(), e -> handlePersistenceException(e, snapshot), connection -> appendSnapshot(connection, snapshot, serializer), connection -> deleteSnapshots( connection, snapshot.getAggregateIdentifier(), snapshot.getSequenceNumber() ) ); } catch (ConcurrencyException e) { // Ignore duplicate key issues in snapshot. It just means a snapshot already exists } }); }
@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()); } }
CounterChangedEvent counterChangedEvent = (CounterChangedEvent) event.getPayload(); if (counterChangedEvent.getCounter() == 1) { commandBus.dispatch(asCommandMessage(new ChangeCounterCommand(domainEvent.getAggregateIdentifier(), counterChangedEvent.getCounter() + 1)), reportErrorCallback); commandBus.dispatch(asCommandMessage(new ChangeCounterCommand(domainEvent.getAggregateIdentifier(), counterChangedEvent.getCounter() + 2)), reportErrorCallback);
CounterChangedEvent counterChangedEvent = (CounterChangedEvent) event.getPayload(); if (counterChangedEvent.getCounter() == 1) { commandBus.dispatch(asCommandMessage(new ChangeCounterCommand(domainEvent.getAggregateIdentifier(), counterChangedEvent.getCounter() + 1)), reportErrorCallback); commandBus.dispatch(asCommandMessage(new ChangeCounterCommand(domainEvent.getAggregateIdentifier(), counterChangedEvent.getCounter() + 2)), reportErrorCallback);