/** * Initialize an aggregate with the given {@code aggregateRoot} which is described in the given * {@code aggregateModel}. The given {@code eventBus} is used to publish events generated by the aggregate. * * @param aggregateRoot The aggregate root instance * @param aggregateModel The model describing the aggregate structure * @param eventBus The EventBus to publish events on * @param <T> The type of the Aggregate root * @return An Aggregate instance, fully initialized */ public static <T> AnnotatedAggregate<T> initialize(T aggregateRoot, AggregateModel<T> aggregateModel, EventBus eventBus) { return initialize(aggregateRoot, aggregateModel, eventBus, null); }
@Override protected void doDeleteWithLock(AnnotatedAggregate<T> aggregate) { EntityManager entityManager = entityManagerProvider.getEntityManager(); entityManager.remove(aggregate.getAggregateRoot()); if (forceFlushOnSave) { entityManager.flush(); } }
@SuppressWarnings("unchecked") @Override public Object handle(Message<?> message) throws Exception { Callable<Object> messageHandling; if (message instanceof CommandMessage) { messageHandling = () -> handle((CommandMessage) message); } else if (message instanceof EventMessage) { messageHandling = () -> handle((EventMessage) message); } else { throw new IllegalArgumentException("Unsupported message type: " + message.getClass()); } return executeWithResult(messageHandling); }
@Override public ApplyMore andThenApply(Supplier<?> payloadOrMessageSupplier) { return andThen(() -> applyMessageOrPayload(payloadOrMessageSupplier.get())); }
/** * Initialize an aggregate created by the given {@code aggregateFactory} which is described in the given * {@code aggregateModel}. The given {@code eventBus} is used to publish events generated by the aggregate. * * @param aggregateFactory The factory to create the aggregate root instance with * @param aggregateModel The model describing the aggregate structure * @param eventBus The EventBus to publish events on * @param repositoryProvider Provides repositories for specific aggregate types * @param generateSequences Whether to generate sequence numbers on events published from this aggregate * @param <T> The type of the Aggregate root * @return An Aggregate instance, fully initialized * * @throws Exception when an error occurs creating the aggregate root instance */ public static <T> AnnotatedAggregate<T> initialize(Callable<T> aggregateFactory, AggregateModel<T> aggregateModel, EventBus eventBus, RepositoryProvider repositoryProvider, boolean generateSequences) throws Exception { AnnotatedAggregate<T> aggregate = new AnnotatedAggregate<>(aggregateModel, eventBus, repositoryProvider); if (generateSequences) { aggregate.initSequence(); } aggregate.registerRoot(aggregateFactory); return aggregate; }
@Test public void testCommandsAreRoutedToCorrectEntity() throws Exception { AggregateModel<Book> bookAggregateModel = AnnotatedAggregateMetaModelFactory.inspectAggregate(Book.class); EventBus mockEventBus = SimpleEventBus.builder().build(); mockEventBus.subscribe(m -> m.forEach(i -> System.out.println(i.getPayloadType().getName()))); AnnotatedAggregate<Book> bookAggregate = AnnotatedAggregate.initialize((Book) null, bookAggregateModel, mockEventBus); bookAggregate.handle(command(new CreateBookCommand("book1"))); bookAggregate.handle(command(new CreatePageCommand("book1"))); bookAggregate.handle(command(new CreateParagraphCommand("book1", 0))); bookAggregate.handle(command(new CreateParagraphCommand("book1", 0))); bookAggregate.handle(command(new UpdateParagraphCommand("book1", 0, 0, "Hello world"))); bookAggregate.handle(command(new UpdateParagraphCommand("book1", 0, 1, "Hello world2"))); assertEquals("Hello world", bookAggregate.getAggregateRoot().getPages().get(0).getParagraphs().get(0).getText()); assertEquals("Hello world2", bookAggregate.getAggregateRoot().getPages().get(0).getParagraphs().get(1).getText()); }
@Override protected <P> ApplyMore doApply(P payload, MetaData metaData) { if (!applying && aggregateRoot != null) { applying = true; try { publish(createMessage(payload, metaData)); while (!delayedTasks.isEmpty()) { delayedTasks.remove().run(); } } finally { delayedTasks.clear(); applying = false; } } else { delayedTasks.add(() -> publish(createMessage(payload, metaData))); } return this; }
@Override protected void publishOnEventBus(EventMessage<?> msg) { if (!initializing) { // force conversion of LazyIdentifierDomainEventMessage to Generic to release reference to Aggregate. super.publishOnEventBus(msg.andMetaData(Collections.emptyMap())); } }
@Override protected void publish(EventMessage<?> msg) { super.publish(msg); snapshotTrigger.eventHandled(msg); if (identifierAsString() == null) { throw new IncompatibleAggregateException("Aggregate identifier must be non-null after applying an event. " + "Make sure the aggregate identifier is initialized at " + "the latest when handling the creation event."); } }
/** * Apply a new event message to the aggregate and then publish this message to external systems. If the given {@code * payloadOrMessage} is an instance of a {@link Message} an event message is applied with the payload and metadata * of the given message, otherwise an event message is applied with given payload and empty metadata. * * @param payloadOrMessage defines the payload and optionally metadata to apply to the aggregate */ protected void applyMessageOrPayload(Object payloadOrMessage) { if (payloadOrMessage instanceof Message) { Message message = (Message) payloadOrMessage; apply(message.getPayload(), message.getMetaData()); } else if (payloadOrMessage != null) { apply(payloadOrMessage, MetaData.emptyInstance()); } }
/** * Initialize an aggregate with the given {@code aggregateRoot} which is described in the given * {@code aggregateModel}. The given {@code eventBus} is used to publish events generated by the aggregate. * * @param aggregateRoot The aggregate root instance * @param aggregateModel The model describing the aggregate structure * @param eventBus The EventBus to publish events on * @param repositoryProvider Provides repositories for specific aggregate types * @param <T> The type of the Aggregate root * @return An Aggregate instance, fully initialized */ public static <T> AnnotatedAggregate<T> initialize(T aggregateRoot, AggregateModel<T> aggregateModel, EventBus eventBus, RepositoryProvider repositoryProvider) { return new AnnotatedAggregate<>(aggregateRoot, aggregateModel, eventBus, repositoryProvider); }
@Override protected <P> ApplyMore doApply(P payload, MetaData metaData) { if (!applying && aggregateRoot != null) { applying = true; try { publish(createMessage(payload, metaData)); while (!delayedTasks.isEmpty()) { delayedTasks.remove().run(); } } finally { delayedTasks.clear(); applying = false; } } else { delayedTasks.add(() -> publish(createMessage(payload, metaData))); } return this; }
/** * Initialize an aggregate created by the given {@code aggregateFactory} which is described in the given * {@code aggregateModel}. The given {@code eventBus} is used to publish events generated by the aggregate. * * @param aggregateFactory The factory to create the aggregate root instance with * @param aggregateModel The model describing the aggregate structure * @param eventBus The EventBus to publish events on * @param repositoryProvider Provides repositories for specific aggregate types * @param generateSequences Whether to generate sequence numbers on events published from this aggregate * @param <T> The type of the Aggregate root * @return An Aggregate instance, fully initialized * * @throws Exception when an error occurs creating the aggregate root instance */ public static <T> AnnotatedAggregate<T> initialize(Callable<T> aggregateFactory, AggregateModel<T> aggregateModel, EventBus eventBus, RepositoryProvider repositoryProvider, boolean generateSequences) throws Exception { AnnotatedAggregate<T> aggregate = new AnnotatedAggregate<>(aggregateModel, eventBus, repositoryProvider); if (generateSequences) { aggregate.initSequence(); } aggregate.registerRoot(aggregateFactory); return aggregate; }
/** * Publish an event to the aggregate root and its entities first and external event handlers (using the given * event bus) later. * * @param msg the event message to publish */ protected void publish(EventMessage<?> msg) { if (msg instanceof DomainEventMessage) { lastKnownSequence = ((DomainEventMessage) msg).getSequenceNumber(); } inspector.publish(msg, aggregateRoot); publishOnEventBus(msg); }
@Override protected void publish(EventMessage<?> msg) { super.publish(msg); snapshotTrigger.eventHandled(msg); if (identifierAsString() == null) { throw new IncompatibleAggregateException("Aggregate identifier must be non-null after applying an event. " + "Make sure the aggregate identifier is initialized at " + "the latest when handling the creation event."); } }
@Override public ApplyMore andThenApply(Supplier<?> payloadOrMessageSupplier) { return andThen(() -> applyMessageOrPayload(payloadOrMessageSupplier.get())); }
/** * Apply a new event message to the aggregate and then publish this message to external systems. If the given {@code * payloadOrMessage} is an instance of a {@link Message} an event message is applied with the payload and metadata * of the given message, otherwise an event message is applied with given payload and empty metadata. * * @param payloadOrMessage defines the payload and optionally metadata to apply to the aggregate */ protected void applyMessageOrPayload(Object payloadOrMessage) { if (payloadOrMessage instanceof Message) { Message message = (Message) payloadOrMessage; apply(message.getPayload(), message.getMetaData()); } else if (payloadOrMessage != null) { apply(payloadOrMessage, MetaData.emptyInstance()); } }
/** * Initialize an aggregate with the given {@code aggregateRoot} which is described in the given * {@code aggregateModel}. The given {@code eventBus} is used to publish events generated by the aggregate. * * @param aggregateRoot The aggregate root instance * @param aggregateModel The model describing the aggregate structure * @param eventBus The EventBus to publish events on * @param repositoryProvider Provides repositories for specific aggregate types * @param <T> The type of the Aggregate root * @return An Aggregate instance, fully initialized */ public static <T> AnnotatedAggregate<T> initialize(T aggregateRoot, AggregateModel<T> aggregateModel, EventBus eventBus, RepositoryProvider repositoryProvider) { return new AnnotatedAggregate<>(aggregateRoot, aggregateModel, eventBus, repositoryProvider); }
/** * Initialize an aggregate created by the given {@code aggregateFactory} which is described in the given * {@code aggregateModel}. The given {@code eventBus} is used to publish events generated by the aggregate. * * @param aggregateFactory The factory to create the aggregate root instance with * @param aggregateModel The model describing the aggregate structure * @param eventBus The EventBus to publish events on * @param repositoryProvider Provides repositories for specific aggregate types * @param <T> The type of the Aggregate root * @return An Aggregate instance, fully initialized * * @throws Exception when an error occurs creating the aggregate root instance */ public static <T> AnnotatedAggregate<T> initialize(Callable<T> aggregateFactory, AggregateModel<T> aggregateModel, EventBus eventBus, RepositoryProvider repositoryProvider) throws Exception { return initialize(aggregateFactory, aggregateModel, eventBus, repositoryProvider, false); }
@SuppressWarnings("unchecked") @Override public Object handle(Message<?> message) throws Exception { Callable<Object> messageHandling; if (message instanceof CommandMessage) { messageHandling = () -> handle((CommandMessage) message); } else if (message instanceof EventMessage) { messageHandling = () -> handle((EventMessage) message); } else { throw new IllegalArgumentException("Unsupported message type: " + message.getClass()); } return executeWithResult(messageHandling); }