/** * 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); }
/** * 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); }
/** * 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 <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) throws Exception { return initialize(aggregateFactory, aggregateModel, eventBus, false); }
/** * 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 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, boolean generateSequences) throws Exception { return initialize(aggregateFactory, aggregateModel, eventBus, null, generateSequences); }
@Override protected AnnotatedAggregate<T> doCreateNewForLock(Callable<T> factoryMethod) throws Exception { // generate sequence numbers in events when using an Event Store return AnnotatedAggregate.initialize(factoryMethod, aggregateModel(), eventBus, repositoryProvider, eventBus instanceof DomainEventSequenceAware); }
@Override public Aggregate<T> newInstance(Callable<T> factoryMethod) throws Exception { Assert.state(storedAggregate == null, () -> "Creating an Aggregate while one is already stored. Test fixtures do not allow multiple instances to be stored."); storedAggregate = AnnotatedAggregate.initialize(factoryMethod, aggregateModel, eventBus, repositoryProvider, true); return storedAggregate; }
@Override protected AnnotatedAggregate<T> doLoadWithLock(String aggregateIdentifier, Long expectedVersion) { T aggregateRoot = entityManagerProvider.getEntityManager().find(getAggregateType(), identifierConverter.apply(aggregateIdentifier), LockModeType.PESSIMISTIC_WRITE); if (aggregateRoot == null) { throw new AggregateNotFoundException(aggregateIdentifier, format("Aggregate [%s] with identifier [%s] not found", getAggregateType().getSimpleName(), aggregateIdentifier)); } AnnotatedAggregate<T> aggregate = AnnotatedAggregate.initialize(aggregateRoot, aggregateModel(), eventBus, repositoryProvider); if (eventBus instanceof DomainEventSequenceAware) { Optional<Long> sequenceNumber = ((DomainEventSequenceAware) eventBus).lastSequenceNumberFor(aggregateIdentifier); sequenceNumber.ifPresent(aggregate::initSequence); } 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()); }
/** * 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); }
/** * 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 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, boolean generateSequences) throws Exception { return initialize(aggregateFactory, aggregateModel, eventBus, null, generateSequences); }
/** * 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 <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) throws Exception { return initialize(aggregateFactory, aggregateModel, eventBus, false); }
/** * 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); }
@Override protected AnnotatedAggregate<T> doCreateNewForLock(Callable<T> factoryMethod) throws Exception { // generate sequence numbers in events when using an Event Store return AnnotatedAggregate.initialize(factoryMethod, aggregateModel(), eventBus, repositoryProvider, eventBus instanceof DomainEventSequenceAware); }
@Override protected AnnotatedAggregate<T> doLoadWithLock(String aggregateIdentifier, Long expectedVersion) { T aggregateRoot = entityManagerProvider.getEntityManager().find(getAggregateType(), identifierConverter.apply(aggregateIdentifier), LockModeType.PESSIMISTIC_WRITE); if (aggregateRoot == null) { throw new AggregateNotFoundException(aggregateIdentifier, format("Aggregate [%s] with identifier [%s] not found", getAggregateType().getSimpleName(), aggregateIdentifier)); } AnnotatedAggregate<T> aggregate = AnnotatedAggregate.initialize(aggregateRoot, aggregateModel(), eventBus, repositoryProvider); if (eventBus instanceof DomainEventSequenceAware) { Optional<Long> sequenceNumber = ((DomainEventSequenceAware) eventBus).lastSequenceNumberFor(aggregateIdentifier); sequenceNumber.ifPresent(aggregate::initSequence); } return aggregate; }