--
You received this message because you are subscribed to the Google Groups "Axon Framework Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to axonframewor...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
public class DemoAggregateRoot extends AbstractAnnotatedAggregateRoot<UUID> {
@AggregateIdentifier
private UUID id;
private List<Message> messages = new ArrayList<>();
protected DemoAggregateRoot() {
super();
}
private DemoAggregateRoot(UUID id) {
apply(new DemoAggregateRootCreatedEvent(id));
}
private void receiveMessage(Message message) {
apply(new MessageReceivedEvent(id, message));
}
@EventHandler
public void on(DemoAggregateRootCreatedEvent event) {
id = event.id;
}
@EventHandler
public void on(MessageReceivedEvent event) {
messages.add(event.message);
}
public static class Handler {
private Repository<DemoAggregateRoot> repository;
void setRepository(Repository<DemoAggregateRoot> repository) {
this.repository = repository;
}
@CommandHandler
public void receiveMessage(ReceiveMessageCommand command) {
DemoAggregateRoot demo = loadOrCreate(command.id);
demo.receiveMessage(command.message);
}
private DemoAggregateRoot loadOrCreate(UUID uuid) {
try {
return repository.load(uuid);
} catch (AggregateNotFoundException e) {
DemoAggregateRoot demo = new DemoAggregateRoot(uuid);
repository.add(demo);
return demo;
}
}
}
}
public class DemoAggregateRootCreatedEvent {
public final UUID id;
public DemoAggregateRootCreatedEvent(UUID id) {
this.id = id;
}
}public class Message {
public final String dummyPayload;
public Message(String dummyPayload) {
this.dummyPayload = dummyPayload;
}
}public class MessageReceivedEvent {
public final UUID id;
public final Message message;
public MessageReceivedEvent(UUID id, Message message) {
this.id = id;
this.message = message;
}
}public class ReceiveMessageCommand {
@TargetAggregateIdentifier
public final UUID id;
public final Message message;
public ReceiveMessageCommand(UUID id, Message message) {
this.id = id;
this.message = message;
}
}public class DemoAggregateRootTest {
private FixtureConfiguration<DemoAggregateRoot> fixture;
private UUID id;
@Before
public void setUp() {
fixture = Fixtures.newGivenWhenThenFixture(DemoAggregateRoot.class);
DemoAggregateRoot.Handler commandHandler = new DemoAggregateRoot.Handler();
commandHandler.setRepository(fixture.getRepository());
fixture.registerAnnotatedCommandHandler(commandHandler);
id = randomUUID();
}
@Test
public void testImplicitCreate() {
Message message = new Message("payload");
fixture.given()
.when(new ReceiveMessageCommand(id, message))
.expectEvents(
new DemoAggregateRootCreatedEvent(id),
new MessageReceivedEvent(id, message)
);
}
@Test
public void testCreateCalledOnlyOnce() {
Message message1 = new Message("one");
Message message2 = new Message("two");
fixture.givenCommands(new ReceiveMessageCommand(id, message1))
.when(new ReceiveMessageCommand(id, message2))
.expectEvents(
new MessageReceivedEvent(id, message2)
);
}
}
--
I've had one issue with the "try a command then recover" approach that's worth pointing out, though it may be a PostgreSQL-specific behavior. When a SQL query returns an error like a unique constraint violation, PostgreSQL sets an error flag on the transaction and won't accept any subsequent write operations. You can work around this by using savepoints, which are sort of like lightweight nested transactions: create a savepoint, try the command, and if it fails, restore the savepoint (which clears the error flag) and proceed with the alternate code path. If the operation succeeds there's no need to do anything special with the savepoint; it's purely a bit of temporary state that goes away once the surrounding transaction is committed or rolled back.
This isn't something unique to Axon; I've had to do the same thing in traditional CRUD apps that needed to do speculative inserts of objects that might already exist. In an Axon application you'll need to make sure you are using the same transaction as the current UnitOfWork but that's not difficult and is well-described in the Axon docs.
-Steve