For the record, this demo is kind of copied from the quickstart but it is in some ways more elaborate and using the latest and greatest version 2.0.7
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
<modelVersion>4.0.0</modelVersion> | |
<groupId>com.pelssers</groupId> | |
<artifactId>axondemo</artifactId> | |
<version>0.0.1-SNAPSHOT</version> | |
<properties> | |
<spring.version>3.1.2.RELEASE</spring.version> | |
<axon.version>2.0.7</axon.version> | |
<junit.version>4.11</junit.version> | |
</properties> | |
<dependencies> | |
<dependency> | |
<groupId>org.axonframework</groupId> | |
<artifactId>axon-core</artifactId> | |
<version>${axon.version}</version> | |
<scope>compile</scope> | |
</dependency> | |
<dependency> | |
<groupId>org.axonframework</groupId> | |
<artifactId>axon-test</artifactId> | |
<version>${axon.version}</version> | |
<scope>test</scope> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework</groupId> | |
<artifactId>spring-context</artifactId> | |
<version>${spring.version}</version> | |
<scope>compile</scope> | |
</dependency> | |
<dependency> | |
<groupId>junit</groupId> | |
<artifactId>junit</artifactId> | |
<version>${junit.version}</version> | |
<scope>test</scope> | |
</dependency> | |
</dependencies> | |
</project> |
Now we start thinking about the behaviour of our little demo application. A user can create a new todo item and he can mark an existing item as completed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package nl.pelssers.axondemo.api; | |
import org.axonframework.commandhandling.annotation.TargetAggregateIdentifier; | |
public class CreateToDoItemCommand { | |
@TargetAggregateIdentifier | |
private final String todoId; | |
private final String description; | |
public CreateToDoItemCommand(String todoId, String description) { | |
this.todoId = todoId; | |
this.description = description; | |
} | |
public String getTodoId() { | |
return todoId; | |
} | |
public String getDescription() { | |
return description; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package nl.pelssers.axondemo.api; | |
import org.axonframework.commandhandling.annotation.TargetAggregateIdentifier; | |
public class MarkCompletedCommand { | |
@TargetAggregateIdentifier | |
private String todoId; | |
public MarkCompletedCommand(String todoId) { | |
this.todoId = todoId; | |
} | |
public String getTodoId() { | |
return todoId; | |
} | |
} |
When commands are issued, something is bound to happen, so we also need to think about what events occur.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package nl.pelssers.axondemo.api; | |
public class ToDoItemCreatedEvent { | |
private final String todoId; | |
private final String description; | |
public ToDoItemCreatedEvent(String todoId, String description) { | |
this.todoId = todoId; | |
this.description = description; | |
} | |
public String getTodoId() { | |
return todoId; | |
} | |
public String getDescription() { | |
return description; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package nl.pelssers.axondemo.api; | |
public class ToDoItemCompletedEvent { | |
private final String todoId; | |
public ToDoItemCompletedEvent(String todoId) { | |
this.todoId = todoId; | |
} | |
public String getTodoId() { | |
return todoId; | |
} | |
} |
We also need to think about our aggregate, in this case our ToDoItem
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package nl.pelssers.axondemo.api; | |
import org.axonframework.commandhandling.annotation.CommandHandler; | |
import org.axonframework.eventhandling.annotation.EventHandler; | |
import org.axonframework.eventsourcing.annotation.AbstractAnnotatedAggregateRoot; | |
import org.axonframework.eventsourcing.annotation.AggregateIdentifier; | |
public class ToDoItem extends AbstractAnnotatedAggregateRoot<ToDoItem>{ | |
private static final long serialVersionUID = 5438310735257852739L; | |
@AggregateIdentifier | |
private String id; | |
public ToDoItem() { | |
} | |
/** | |
* This is a constructor annotated with a CommandHandler. It makes sense that a ToDoItem | |
* instance gets created whenever a CreateToDoItemCommand is issued. The command handler | |
* creates a ToDoItemCreatedEvent which gets registered with the EventContainer by calling apply | |
* @param command | |
*/ | |
@CommandHandler | |
public ToDoItem(CreateToDoItemCommand command) { | |
apply(new ToDoItemCreatedEvent(command.getTodoId(), command.getDescription())); | |
} | |
@EventHandler | |
public void on(ToDoItemCreatedEvent event) { | |
this.id = event.getTodoId(); | |
} | |
@CommandHandler | |
public void markCompleted(MarkCompletedCommand command) { | |
apply(new ToDoItemCompletedEvent(id)); | |
} | |
} |
Of course we need to test if events are properly created when a command is issued.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package nl.pelssers.axondemo; | |
import nl.pelssers.axondemo.api.CreateToDoItemCommand; | |
import nl.pelssers.axondemo.api.MarkCompletedCommand; | |
import nl.pelssers.axondemo.api.ToDoItem; | |
import nl.pelssers.axondemo.api.ToDoItemCompletedEvent; | |
import nl.pelssers.axondemo.api.ToDoItemCreatedEvent; | |
import org.axonframework.test.FixtureConfiguration; | |
import org.axonframework.test.Fixtures; | |
import org.junit.Before; | |
import org.junit.Test; | |
public class ToDoItemTest { | |
private FixtureConfiguration<ToDoItem> fixture; | |
private final static String ITEM1_ID = "TODO-1"; | |
private final static String ITEM1_DESCRIPTION = "Clean house"; | |
@Before | |
public void setUp() throws Exception { | |
fixture = Fixtures.newGivenWhenThenFixture(ToDoItem.class); | |
} | |
@Test | |
public void testCreateToDoItem() throws Exception { | |
fixture | |
.given() | |
.when(new CreateToDoItemCommand(ITEM1_ID, ITEM1_DESCRIPTION)) | |
.expectEvents(new ToDoItemCreatedEvent(ITEM1_ID, ITEM1_DESCRIPTION)); | |
} | |
@Test | |
public void testMarkToDoItemAsCompleted() throws Exception { | |
fixture | |
.given(new ToDoItemCreatedEvent(ITEM1_ID, ITEM1_DESCRIPTION)) | |
.when(new MarkCompletedCommand(ITEM1_ID)) | |
.expectEvents(new ToDoItemCompletedEvent(ITEM1_ID)); | |
} | |
} |
We also need to configure the application, as usual we will use Spring to wire our app.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package nl.pelssers.axondemo.configuration; | |
import java.io.File; | |
import nl.pelssers.axondemo.ToDoEventHandler; | |
import nl.pelssers.axondemo.api.ToDoItem; | |
import org.axonframework.commandhandling.CommandBus; | |
import org.axonframework.commandhandling.SimpleCommandBus; | |
import org.axonframework.commandhandling.annotation.AggregateAnnotationCommandHandler; | |
import org.axonframework.commandhandling.gateway.CommandGateway; | |
import org.axonframework.commandhandling.gateway.DefaultCommandGateway; | |
import org.axonframework.eventhandling.EventBus; | |
import org.axonframework.eventhandling.SimpleEventBus; | |
import org.axonframework.eventhandling.annotation.AnnotationEventListenerAdapter; | |
import org.axonframework.eventsourcing.EventSourcingRepository; | |
import org.axonframework.eventstore.EventStore; | |
import org.axonframework.eventstore.fs.FileSystemEventStore; | |
import org.axonframework.eventstore.fs.SimpleEventFileResolver; | |
import org.springframework.context.annotation.Bean; | |
import org.springframework.context.annotation.Configuration; | |
@Configuration | |
public class AxonDemoConfiguration { | |
@Bean | |
public CommandBus commandBus() { | |
return new SimpleCommandBus(); | |
} | |
@Bean | |
public CommandGateway commandGateway() { | |
return new DefaultCommandGateway(commandBus()); | |
} | |
@Bean | |
public EventStore eventStore() { | |
return new FileSystemEventStore(new SimpleEventFileResolver(new File("c:/tmp/axondemo"))); | |
} | |
@Bean | |
public EventBus eventBus() { | |
return new SimpleEventBus(); | |
} | |
@Bean | |
public EventSourcingRepository<ToDoItem> todoRepository() { | |
EventSourcingRepository<ToDoItem> repository = new EventSourcingRepository<ToDoItem>(ToDoItem.class, eventStore()); | |
repository.setEventBus(eventBus()); | |
return repository; | |
} | |
@SuppressWarnings("unchecked") | |
@Bean | |
public AggregateAnnotationCommandHandler<ToDoItem> toDoItemHandler() { | |
return AggregateAnnotationCommandHandler.subscribe(ToDoItem.class, todoRepository(), commandBus()); | |
} | |
@Bean | |
public AnnotationEventListenerAdapter todoEventHandler() { | |
return AnnotationEventListenerAdapter.subscribe(new ToDoEventHandler(), eventBus()); | |
} | |
} |
The demo app just issues 2 commands, mimicking what a user might do. And to make it interesting we also create an event handler for our ToDoItem events.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package nl.pelssers.axondemo; | |
import nl.pelssers.axondemo.api.CreateToDoItemCommand; | |
import nl.pelssers.axondemo.api.MarkCompletedCommand; | |
import nl.pelssers.axondemo.configuration.AxonDemoConfiguration; | |
import org.axonframework.commandhandling.gateway.CommandGateway; | |
import org.springframework.context.ApplicationContext; | |
import org.springframework.context.annotation.AnnotationConfigApplicationContext; | |
import org.springframework.stereotype.Component; | |
@Component | |
public class ToDoApplication { | |
private CommandGateway commandGateway; | |
public ToDoApplication(CommandGateway commandGateway) { | |
this.commandGateway = commandGateway; | |
} | |
/** | |
* @param args | |
*/ | |
public static void main(String[] args) { | |
ApplicationContext context = new AnnotationConfigApplicationContext(AxonDemoConfiguration.class); | |
ToDoApplication todoApplication = new ToDoApplication(context.getBean(CommandGateway.class)); | |
todoApplication.run(); | |
} | |
private void run() { | |
final String itemId = "TODO-1"; | |
commandGateway.send(new CreateToDoItemCommand(itemId, "Clean house")); | |
commandGateway.send(new MarkCompletedCommand(itemId)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package nl.pelssers.axondemo; | |
import org.axonframework.eventhandling.annotation.EventHandler; | |
import nl.pelssers.axondemo.api.ToDoItemCompletedEvent; | |
import nl.pelssers.axondemo.api.ToDoItemCreatedEvent; | |
public class ToDoEventHandler { | |
@EventHandler | |
public void handle(ToDoItemCreatedEvent event) { | |
System.out.println("We've got something to do: " + event.getDescription() + " (" + event.getTodoId() + ")"); | |
} | |
@EventHandler | |
public void handle(ToDoItemCompletedEvent event) { | |
System.out.println("We've completed a task: " + event.getTodoId()); | |
} | |
} |
When we run the ToDoApplication we notice a file gets created in c:/tmp/axondemo/ for our aggregate called
TODO-1.events. Also we nicely see some output appearing in the console:
We've got something to do: Clean house (TODO-1) We've completed a task: TODO-1
No comments:
Post a Comment