├── .gitignore ├── .java-options ├── .java-version ├── build.gradle ├── conference-client └── src │ └── main │ └── groovy │ └── example │ └── conference │ └── client │ ├── ConferenceClient.groovy │ ├── ConferenceClientConstants.groovy │ └── ConferenceClientUtils.groovy ├── conference-configuration └── src │ └── main │ ├── java │ └── example │ │ └── conference │ │ └── configuration │ │ ├── ConferenceConfiguration.java │ │ ├── DefaultAuditLogger.java │ │ └── EventAuditListener.java │ └── resources │ └── example │ └── conference │ └── configuration │ ├── configuration.properties │ ├── configuration.xml │ └── logback.xml ├── conference-shared └── src │ └── main │ ├── groovy │ └── example │ │ └── conference │ │ └── shared │ │ ├── Command.groovy │ │ ├── Event.groovy │ │ └── ValueObject.groovy │ └── java │ └── example │ └── conference │ └── shared │ └── Money.java ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── management-api └── src │ └── main │ └── groovy │ └── example │ └── conference │ └── management │ └── api │ ├── ConferenceCreated.groovy │ ├── ConferenceUpdated.groovy │ ├── SeatTypeCreated.groovy │ └── SeatTypeUpdated.groovy ├── management-impl └── src │ └── main │ ├── java │ └── example │ │ └── conference │ │ └── management │ │ └── impl │ │ ├── ManagementApplication.java │ │ └── domain │ │ ├── Conference.java │ │ ├── ConferenceEventHandler.java │ │ ├── ConferenceRepository.java │ │ ├── SeatType.java │ │ ├── SeatTypeEventHandler.java │ │ └── SeatTypeRepository.java │ └── resources │ ├── application.yml │ └── logback.xml ├── payments-api └── src │ └── main │ └── groovy │ └── example │ └── conference │ └── payments │ └── api │ ├── MakePayment.groovy │ └── PaymentReceived.groovy ├── payments-impl └── src │ └── main │ ├── java │ └── example │ │ └── conference │ │ └── payments │ │ └── impl │ │ ├── PaymentsApplication.java │ │ ├── controllers │ │ └── PaymentController.java │ │ ├── domain │ │ └── Payment.java │ │ └── handlers │ │ └── PaymentHandler.java │ └── resources │ ├── application.yml │ ├── logback.xml │ └── payments.xml ├── registrations-api └── src │ └── main │ └── groovy │ └── example │ └── conference │ └── registrations │ └── api │ ├── AddSeats.groovy │ ├── CancelSeatsReservation.groovy │ ├── ConfirmOrder.groovy │ ├── CreateSeatsAvailability.groovy │ ├── MakeSeatsReservation.groovy │ ├── OrderConfirmed.groovy │ ├── OrderPlaced.groovy │ ├── RegisterToConference.groovy │ ├── RegistrationExpired.groovy │ ├── RejectOrder.groovy │ ├── RemoveSeats.groovy │ ├── SeatQuantity.groovy │ ├── SeatsAvailabilityChanged.groovy │ ├── SeatsAvailabilityCreated.groovy │ ├── SeatsReservationAccepted.groovy │ ├── SeatsReservationCancelled.groovy │ └── SeatsReservationRejected.groovy ├── registrations-impl └── src │ └── main │ ├── java │ └── example │ │ └── conference │ │ └── registrations │ │ └── impl │ │ ├── RegistrationsApplication.java │ │ ├── controllers │ │ └── RegistrationsController.java │ │ ├── domain │ │ ├── Order.java │ │ ├── OrderItem.java │ │ ├── RegistrationSaga.java │ │ └── SeatsAvailability.java │ │ └── handlers │ │ ├── OrderEventHandler.java │ │ ├── RegistrationEventHandler.java │ │ └── SeatsAvailabilityHandler.java │ └── resources │ ├── application.yml │ ├── logback.xml │ └── registrations.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | .idea 3 | build 4 | classes 5 | 6 | *.iml 7 | *.ipr 8 | *.iws 9 | 10 | -------------------------------------------------------------------------------- /.java-options: -------------------------------------------------------------------------------- 1 | -Xmx256m 2 | -------------------------------------------------------------------------------- /.java-version: -------------------------------------------------------------------------------- 1 | oracle64-1.8.0.05 2 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | maven { url "http://repo.spring.io/libs-snapshot" } 4 | mavenLocal() 5 | } 6 | dependencies { 7 | classpath('org.springframework.boot:spring-boot-gradle-plugin:1.0.2.RELEASE') 8 | } 9 | } 10 | 11 | configure(allprojects) { project -> 12 | group = 'example-axon' 13 | version = '1.0' 14 | 15 | ext { 16 | axonVersion = '2.1.2' 17 | groovyVersion = '2.3.0' 18 | springVersion = '4.0.3.RELEASE' 19 | springAmqpVersion = '1.2.1.RELEASE' 20 | springBootVersion = '1.0.2.RELEASE' 21 | } 22 | 23 | repositories { 24 | mavenCentral() 25 | } 26 | 27 | apply plugin: "java" 28 | 29 | compileJava { 30 | sourceCompatibility = 1.8 31 | targetCompatibility = 1.8 32 | } 33 | 34 | compileTestJava { 35 | sourceCompatibility = 1.8 36 | targetCompatibility = 1.8 37 | } 38 | 39 | } 40 | 41 | configure(subprojects.findAll { it.name =~ '-api' }) { 42 | apply plugin: 'groovy' 43 | 44 | dependencies { 45 | compile project(':conference-shared') 46 | 47 | compile("org.codehaus.groovy:groovy-all:${groovyVersion}") 48 | } 49 | } 50 | 51 | configure(subprojects.findAll { it.name =~ '-impl' }) { 52 | apply plugin: 'spring-boot' 53 | 54 | dependencies { 55 | compile project(':conference-shared') 56 | compile project(':conference-configuration') 57 | 58 | compile project(':management-api') 59 | compile project(':payments-api') 60 | compile project(':registrations-api') 61 | 62 | compile('org.springframework.boot:spring-boot-starter-web') 63 | } 64 | } 65 | 66 | 67 | project(':conference-client') { 68 | apply plugin: 'groovy' 69 | 70 | dependencies { 71 | compile("org.codehaus.groovy:groovy-all:${groovyVersion}") 72 | compile("com.jayway.restassured:rest-assured:2.3.1") { 73 | exclude group: "org.codehaus.groovy", module: 'groovy' 74 | } 75 | } 76 | } 77 | 78 | project(':conference-configuration') { 79 | dependencies { 80 | compile("org.springframework:spring-context:${springVersion}") 81 | compile("org.springframework.amqp:spring-rabbit:${springAmqpVersion}") 82 | compile("org.springframework.boot:spring-boot:${springBootVersion}") 83 | 84 | compile("org.axonframework:axon-core:${axonVersion}") 85 | compile("org.axonframework:axon-amqp:${axonVersion}") 86 | } 87 | } 88 | 89 | project(':conference-shared') { 90 | apply plugin: 'groovy' 91 | 92 | dependencies { 93 | compile("org.codehaus.groovy:groovy-all:${groovyVersion}") 94 | } 95 | } 96 | 97 | project(':management-impl') { 98 | dependencies { 99 | compile("org.springframework.boot:spring-boot-starter-data-jpa") 100 | compile("org.springframework.boot:spring-boot-starter-data-rest") 101 | compile("com.h2database:h2") 102 | } 103 | } 104 | 105 | project(':payments-impl') { 106 | dependencies { 107 | // TODO 108 | } 109 | } 110 | 111 | project(':registrations-impl') { 112 | dependencies { 113 | // TODO 114 | } 115 | } 116 | 117 | task wrapper(type: Wrapper) { 118 | gradleVersion = '1.11' 119 | } 120 | -------------------------------------------------------------------------------- /conference-client/src/main/groovy/example/conference/client/ConferenceClient.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.client 2 | 3 | import com.jayway.restassured.RestAssured 4 | import com.jayway.restassured.http.ContentType 5 | import org.apache.http.HttpHeaders 6 | import org.apache.http.HttpStatus 7 | 8 | import static com.jayway.restassured.RestAssured.given 9 | import static example.conference.client.ConferenceClientConstants.* 10 | import static example.conference.client.ConferenceClientUtils.* 11 | 12 | RestAssured.requestContentType(ContentType.JSON) 13 | 14 | def qconId = generateId() 15 | def qconHref = createConference(qconId, 'QCon 2014', 'New York') 16 | 17 | waitForUser() 18 | 19 | def qconConferenceId = generateId() 20 | createSeatType(qconConferenceId, 'Conference', 800, 1600, 'USD', qconHref) 21 | 22 | waitForUser() 23 | 24 | def qconTutorialsId = generateId() 25 | createSeatType(qconTutorialsId, 'Tutorials', 200, 300, 'USD', qconHref) 26 | 27 | waitForUser() 28 | 29 | publishConference(qconId) 30 | 31 | waitForUser() 32 | 33 | def qconOrderId = generateId() 34 | registerToConference(qconOrderId, qconId, 35 | [ 36 | [seatTypeId: qconConferenceId, quantity: 3], 37 | [seatTypeId: qconTutorialsId, quantity: 1], 38 | ] 39 | ) 40 | 41 | waitForUser() 42 | 43 | def paymentId = generateId() 44 | makePayment(paymentId, qconOrderId) 45 | 46 | waitForUser() 47 | 48 | def createConference(String conferenceId, String name, String location) { 49 | println "Create Conference: $name" 50 | 51 | def conference = [ 52 | id : conferenceId, 53 | name : name, 54 | location: location, 55 | published: false 56 | ] 57 | 58 | given() 59 | .port(MANAGEMENT_PORT) 60 | .body(toJson(conference)) 61 | .post("/conferences") 62 | .then() 63 | .assertThat().statusCode(HttpStatus.SC_CREATED) 64 | .extract().header(HttpHeaders.LOCATION) 65 | } 66 | 67 | def createSeatType(String seatTypeId, String type, int quantity, int priceAmount, String priceCurrency, String conferenceHref) { 68 | println "Create SeatType: $type" 69 | 70 | def seatType = [ 71 | id : seatTypeId, 72 | type : type, 73 | quantity : quantity, 74 | priceAmount : priceAmount, 75 | priceCurrency: priceCurrency, 76 | conference : conferenceHref 77 | ] 78 | 79 | given() 80 | .port(MANAGEMENT_PORT) 81 | .body(toJson(seatType)) 82 | .post("/seatTypes") 83 | .then() 84 | .assertThat().statusCode(HttpStatus.SC_CREATED) 85 | .extract().header(HttpHeaders.LOCATION) 86 | } 87 | 88 | def publishConference(String conferenceId) { 89 | println "Publish Conference: $conferenceId" 90 | 91 | def conference = [ 92 | published: true 93 | ] 94 | 95 | given() 96 | .port(MANAGEMENT_PORT) 97 | .body(toJson(conference)) 98 | .patch("/conferences/$conferenceId") 99 | .then() 100 | .assertThat().statusCode(HttpStatus.SC_NO_CONTENT) 101 | .extract().header(HttpHeaders.LOCATION) 102 | } 103 | 104 | def registerToConference(String orderId, String conferenceId, def seats) { 105 | println "Register to conference: $conferenceId, $seats" 106 | 107 | def registerToConference = [ 108 | orderId : orderId, 109 | conferenceId: conferenceId, 110 | seats : seats 111 | ] 112 | 113 | given() 114 | .port(REGISTRATIONS_PORT) 115 | .body(toJson(registerToConference)) 116 | .post("/registerToConference") 117 | .then() 118 | .assertThat().statusCode(HttpStatus.SC_CREATED) 119 | } 120 | 121 | def makePayment(String paymentId, String orderId) { 122 | println "Make payment for order: $orderId" 123 | 124 | def makePayment = [ 125 | paymentId: paymentId, 126 | orderId: orderId 127 | ] 128 | 129 | given() 130 | .port(PAYMENTS_PORT) 131 | .body(toJson(makePayment)) 132 | .post("/makePayment") 133 | .then() 134 | .assertThat().statusCode(HttpStatus.SC_CREATED) 135 | } -------------------------------------------------------------------------------- /conference-client/src/main/groovy/example/conference/client/ConferenceClientConstants.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.client; 2 | 3 | interface ConferenceClientConstants { 4 | int MANAGEMENT_PORT = 18080; 5 | int REGISTRATIONS_PORT = 28080; 6 | int PAYMENTS_PORT = 38080; 7 | } 8 | -------------------------------------------------------------------------------- /conference-client/src/main/groovy/example/conference/client/ConferenceClientUtils.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.client 2 | 3 | import groovy.json.JsonBuilder 4 | 5 | class ConferenceClientUtils { 6 | 7 | static def toJson(Object object) { 8 | new JsonBuilder(object).toPrettyString() 9 | } 10 | 11 | static def generateId() { 12 | UUID.randomUUID().toString(); 13 | } 14 | 15 | static def waitForUser() { 16 | print 'Pres any key to continue' 17 | System.in.newReader().readLine() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /conference-configuration/src/main/java/example/conference/configuration/ConferenceConfiguration.java: -------------------------------------------------------------------------------- 1 | package example.conference.configuration; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.context.annotation.ImportResource; 6 | import org.springframework.context.annotation.PropertySource; 7 | 8 | @Configuration 9 | @PropertySource("classpath:/example/conference/configuration/configuration.properties") 10 | @ImportResource("classpath:/example/conference/configuration/configuration.xml") 11 | public class ConferenceConfiguration { 12 | @Bean 13 | public EventAuditListener eventAuditListener() { 14 | return new EventAuditListener(); 15 | } 16 | } -------------------------------------------------------------------------------- /conference-configuration/src/main/java/example/conference/configuration/DefaultAuditLogger.java: -------------------------------------------------------------------------------- 1 | package example.conference.configuration; 2 | 3 | import org.axonframework.auditing.AuditLogger; 4 | import org.axonframework.commandhandling.GenericCommandMessage; 5 | import org.axonframework.domain.EventMessage; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.util.List; 10 | 11 | public class DefaultAuditLogger implements AuditLogger { 12 | 13 | private static final Logger logger = LoggerFactory.getLogger(DefaultAuditLogger.class); 14 | 15 | @Override 16 | public void logSuccessful(Object command, Object returnValue, List events) { 17 | if (logger.isTraceEnabled()) { 18 | Object payload = extractCommandPayload(command); 19 | StringBuilder sb = new StringBuilder() 20 | .append("Successful execution of command: ") 21 | .append("\n\t ").append(payload.toString()); 22 | 23 | if (returnValue != null) { 24 | sb.append("\n\t Return value: ").append(returnValue.toString()); 25 | } 26 | 27 | if (events != null && events.size() > 0) { 28 | sb.append("\n\t List of events fired during execution:"); 29 | for (EventMessage event : events) { 30 | sb.append("\n\t\t ").append(event.getPayload().toString()); 31 | } 32 | } 33 | 34 | logger.trace(sb.toString()); 35 | } 36 | } 37 | 38 | @Override 39 | public void logFailed(Object command, Throwable failureCause, List events) { 40 | if (logger.isTraceEnabled()) { 41 | Object payload = extractCommandPayload(command); 42 | 43 | StringBuilder sb = new StringBuilder() 44 | .append("Failure occurred during execution of command: ") 45 | .append("\n\t ").append(payload.toString()) 46 | .append("\n\t Exception name: ") 47 | .append(failureCause.getClass().getName()) 48 | .append("\n\t Exception message: ") 49 | .append(failureCause.getMessage()); 50 | if (events != null && events.size() > 0) { 51 | sb.append("\n\t List of events fired during execution:"); 52 | for (EventMessage event : events) { 53 | sb.append("\n\t\t ").append(event.getPayload().toString()); 54 | } 55 | } 56 | 57 | logger.trace(sb.toString(), failureCause); 58 | } 59 | } 60 | 61 | private Object extractCommandPayload(Object command) { 62 | if (command instanceof GenericCommandMessage) { 63 | return ((GenericCommandMessage) command).getPayload(); 64 | } else { 65 | return command; 66 | } 67 | } 68 | } 69 | 70 | -------------------------------------------------------------------------------- /conference-configuration/src/main/java/example/conference/configuration/EventAuditListener.java: -------------------------------------------------------------------------------- 1 | package example.conference.configuration; 2 | 3 | import org.axonframework.domain.EventMessage; 4 | import org.axonframework.eventhandling.annotation.EventHandler; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | public class EventAuditListener { 9 | 10 | private static final Logger logger = LoggerFactory.getLogger(EventAuditListener.class); 11 | 12 | @EventHandler 13 | public void audit(EventMessage event) { 14 | if (logger.isTraceEnabled()) { 15 | logger.trace("Domain event published: {}", event.getPayload().toString()); 16 | } 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /conference-configuration/src/main/resources/example/conference/configuration/configuration.properties: -------------------------------------------------------------------------------- 1 | application.configuration.rabbit.host=localhost 2 | application.configuration.rabbit.port=5672 3 | application.configuration.rabbit.username=guest 4 | application.configuration.rabbit.password=guest 5 | application.configuration.rabbit.exchangeName=Axon.EventBus -------------------------------------------------------------------------------- /conference-configuration/src/main/resources/example/conference/configuration/configuration.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 18 | 19 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /conference-configuration/src/main/resources/example/conference/configuration/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /conference-shared/src/main/groovy/example/conference/shared/Command.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.shared 2 | 3 | import groovy.transform.AnnotationCollector 4 | import groovy.transform.EqualsAndHashCode 5 | import groovy.transform.ToString 6 | import groovy.transform.TupleConstructor 7 | import groovy.transform.builder.Builder 8 | 9 | //@Builder http://jira.codehaus.org/browse/GROOVY-6774 10 | @TupleConstructor 11 | @EqualsAndHashCode 12 | @ToString(includePackage = false, includeNames = true) 13 | @AnnotationCollector() 14 | @interface Command { 15 | } 16 | -------------------------------------------------------------------------------- /conference-shared/src/main/groovy/example/conference/shared/Event.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.shared 2 | 3 | import groovy.transform.AnnotationCollector 4 | import groovy.transform.EqualsAndHashCode 5 | import groovy.transform.ToString 6 | import groovy.transform.TupleConstructor 7 | import groovy.transform.builder.Builder 8 | 9 | //@Builder http://jira.codehaus.org/browse/GROOVY-6774 10 | @TupleConstructor 11 | @EqualsAndHashCode 12 | @ToString(includePackage = false, includeNames = true) 13 | @AnnotationCollector() 14 | @interface Event { 15 | } 16 | -------------------------------------------------------------------------------- /conference-shared/src/main/groovy/example/conference/shared/ValueObject.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.shared 2 | 3 | import groovy.transform.AnnotationCollector 4 | import groovy.transform.EqualsAndHashCode 5 | import groovy.transform.ToString 6 | import groovy.transform.TupleConstructor 7 | 8 | //@Builder http://jira.codehaus.org/browse/GROOVY-6774 9 | @TupleConstructor 10 | @EqualsAndHashCode 11 | @ToString(includePackage = false, includeNames = true) 12 | @AnnotationCollector() 13 | @interface ValueObject { 14 | } 15 | -------------------------------------------------------------------------------- /conference-shared/src/main/java/example/conference/shared/Money.java: -------------------------------------------------------------------------------- 1 | package example.conference.shared; 2 | 3 | import java.math.BigDecimal; 4 | 5 | import static java.util.Objects.requireNonNull; 6 | 7 | public class Money { 8 | 9 | private BigDecimal amount; 10 | 11 | private String currency; 12 | 13 | public Money(BigDecimal amount, String currency) { 14 | this.amount = requireNonNull(amount); 15 | this.currency = requireNonNull(currency); 16 | } 17 | 18 | public BigDecimal getAmount() { 19 | return amount; 20 | } 21 | 22 | public String getCurrency() { 23 | return currency; 24 | } 25 | 26 | @Override 27 | public boolean equals(Object o) { 28 | if (this == o) return true; 29 | if (o == null || getClass() != o.getClass()) return false; 30 | 31 | Money money = (Money) o; 32 | 33 | if (amount != null ? !amount.equals(money.amount) : money.amount != null) return false; 34 | if (currency != null ? !currency.equals(money.currency) : money.currency != null) return false; 35 | 36 | return true; 37 | } 38 | 39 | @Override 40 | public int hashCode() { 41 | int result = amount != null ? amount.hashCode() : 0; 42 | result = 31 * result + (currency != null ? currency.hashCode() : 0); 43 | return result; 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | return amount + " " + currency; 49 | } 50 | 51 | protected Money() { 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkuthan/example-axon/06d75f893b1e010ffc9acdca8acc7ea4fa7e4f49/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Apr 30 19:43:59 CEST 2014 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=http\://services.gradle.org/distributions/gradle-1.11-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /management-api/src/main/groovy/example/conference/management/api/ConferenceCreated.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.management.api 2 | 3 | import example.conference.shared.Event 4 | 5 | @Event 6 | class ConferenceCreated { 7 | String conferenceId 8 | String name 9 | String location 10 | boolean published 11 | } 12 | -------------------------------------------------------------------------------- /management-api/src/main/groovy/example/conference/management/api/ConferenceUpdated.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.management.api 2 | 3 | import example.conference.shared.Event 4 | 5 | @Event 6 | class ConferenceUpdated { 7 | String conferenceId 8 | String name 9 | String location 10 | boolean published 11 | } 12 | -------------------------------------------------------------------------------- /management-api/src/main/groovy/example/conference/management/api/SeatTypeCreated.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.management.api 2 | 3 | import example.conference.shared.Event 4 | import example.conference.shared.Money 5 | 6 | @Event 7 | class SeatTypeCreated { 8 | String seatTypeId 9 | String type 10 | int quantity 11 | Money price 12 | String conferenceId; 13 | } 14 | -------------------------------------------------------------------------------- /management-api/src/main/groovy/example/conference/management/api/SeatTypeUpdated.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.management.api 2 | 3 | import example.conference.shared.Event 4 | import example.conference.shared.Money 5 | 6 | @Event 7 | class SeatTypeUpdated { 8 | String seatTypeId 9 | String type 10 | int quantity 11 | Money price 12 | String conferenceId; 13 | } 14 | -------------------------------------------------------------------------------- /management-impl/src/main/java/example/conference/management/impl/ManagementApplication.java: -------------------------------------------------------------------------------- 1 | package example.conference.management.impl; 2 | 3 | import example.conference.configuration.ConferenceConfiguration; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 6 | import org.springframework.context.annotation.ComponentScan; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.context.annotation.Import; 9 | import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration; 10 | 11 | @Configuration 12 | @EnableAutoConfiguration 13 | @Import({ConferenceConfiguration.class, RepositoryRestMvcConfiguration.class}) 14 | @ComponentScan 15 | public class ManagementApplication { 16 | 17 | public static void main(String[] args) { 18 | SpringApplication.run(ManagementApplication.class, args); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /management-impl/src/main/java/example/conference/management/impl/domain/Conference.java: -------------------------------------------------------------------------------- 1 | package example.conference.management.impl.domain; 2 | 3 | import javax.persistence.*; 4 | import java.util.Set; 5 | 6 | @Entity 7 | public class Conference { 8 | 9 | @Id 10 | private String id; 11 | 12 | @Basic(optional = false) 13 | private String name; 14 | 15 | @Basic(optional = false) 16 | private String location; 17 | 18 | @Basic(optional = false) 19 | private Boolean published; 20 | 21 | @OneToMany(fetch = FetchType.LAZY, mappedBy = "conference", cascade = CascadeType.ALL, orphanRemoval = true) 22 | private Set seatTypes; 23 | 24 | public String getId() { 25 | return id; 26 | } 27 | 28 | public void setId(String id) { 29 | this.id = id; 30 | } 31 | 32 | public String getName() { 33 | return name; 34 | } 35 | 36 | public void setName(String name) { 37 | this.name = name; 38 | } 39 | 40 | public String getLocation() { 41 | return location; 42 | } 43 | 44 | public void setLocation(String location) { 45 | this.location = location; 46 | } 47 | 48 | public Boolean getPublished() { 49 | return published; 50 | } 51 | 52 | public void setPublished(Boolean published) { 53 | this.published = published; 54 | } 55 | 56 | public Set getSeatTypes() { 57 | return seatTypes; 58 | } 59 | 60 | public void setSeatTypes(Set seatTypes) { 61 | this.seatTypes = seatTypes; 62 | } 63 | 64 | protected Conference() { 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /management-impl/src/main/java/example/conference/management/impl/domain/ConferenceEventHandler.java: -------------------------------------------------------------------------------- 1 | package example.conference.management.impl.domain; 2 | 3 | import example.conference.management.api.ConferenceCreated; 4 | import example.conference.management.api.ConferenceUpdated; 5 | import org.axonframework.domain.GenericEventMessage; 6 | import org.axonframework.eventhandling.EventBus; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.data.rest.core.annotation.HandleAfterCreate; 9 | import org.springframework.data.rest.core.annotation.HandleAfterSave; 10 | import org.springframework.data.rest.core.annotation.RepositoryEventHandler; 11 | import org.springframework.stereotype.Component; 12 | 13 | @Component 14 | @RepositoryEventHandler(Conference.class) 15 | public class ConferenceEventHandler { 16 | 17 | @Autowired 18 | private EventBus eventBus; 19 | 20 | @HandleAfterCreate 21 | public void publishConferenceCreatedEvent(Conference conference) { 22 | ConferenceCreated event = new ConferenceCreated(); 23 | 24 | event.setConferenceId(conference.getId()); 25 | event.setName(conference.getName()); 26 | event.setLocation(conference.getLocation()); 27 | event.setPublished(conference.getPublished()); 28 | 29 | publishEvent(event); 30 | } 31 | 32 | @HandleAfterSave 33 | public void publishConferenceUpdatedEvent(Conference conference) { 34 | ConferenceUpdated event = new ConferenceUpdated(); 35 | 36 | event.setConferenceId(conference.getId()); 37 | event.setName(conference.getName()); 38 | event.setLocation(conference.getLocation()); 39 | event.setPublished(conference.getPublished()); 40 | 41 | publishEvent(event); 42 | } 43 | 44 | private void publishEvent(Object event) { 45 | eventBus.publish(GenericEventMessage.asEventMessage(event)); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /management-impl/src/main/java/example/conference/management/impl/domain/ConferenceRepository.java: -------------------------------------------------------------------------------- 1 | package example.conference.management.impl.domain; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 5 | import org.springframework.data.rest.core.annotation.RestResource; 6 | import org.springframework.stereotype.Repository; 7 | 8 | @RepositoryRestResource 9 | public interface ConferenceRepository extends JpaRepository { 10 | } 11 | -------------------------------------------------------------------------------- /management-impl/src/main/java/example/conference/management/impl/domain/SeatType.java: -------------------------------------------------------------------------------- 1 | package example.conference.management.impl.domain; 2 | 3 | 4 | import javax.persistence.*; 5 | import java.math.BigDecimal; 6 | 7 | @Entity 8 | public class SeatType { 9 | 10 | @Id 11 | private String id; 12 | 13 | @Basic(optional = false) 14 | private String type; 15 | 16 | @Basic(optional = false) 17 | private int quantity; 18 | 19 | @Basic(optional = false) 20 | private BigDecimal priceAmount; 21 | 22 | @Basic(optional = false) 23 | private String priceCurrency; 24 | 25 | @ManyToOne(fetch = FetchType.LAZY, optional = false) 26 | private Conference conference; 27 | 28 | public String getId() { 29 | return id; 30 | } 31 | 32 | public void setId(String id) { 33 | this.id = id; 34 | } 35 | 36 | public String getType() { 37 | return type; 38 | } 39 | 40 | public void setType(String type) { 41 | this.type = type; 42 | } 43 | 44 | public int getQuantity() { 45 | return quantity; 46 | } 47 | 48 | public void setQuantity(int quantity) { 49 | this.quantity = quantity; 50 | } 51 | 52 | public BigDecimal getPriceAmount() { 53 | return priceAmount; 54 | } 55 | 56 | public void setPriceAmount(BigDecimal priceAmount) { 57 | this.priceAmount = priceAmount; 58 | } 59 | 60 | public String getPriceCurrency() { 61 | return priceCurrency; 62 | } 63 | 64 | public void setPriceCurrency(String priceCurrency) { 65 | this.priceCurrency = priceCurrency; 66 | } 67 | 68 | public Conference getConference() { 69 | return conference; 70 | } 71 | 72 | public void setConference(Conference conference) { 73 | this.conference = conference; 74 | } 75 | 76 | protected SeatType() { 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /management-impl/src/main/java/example/conference/management/impl/domain/SeatTypeEventHandler.java: -------------------------------------------------------------------------------- 1 | package example.conference.management.impl.domain; 2 | 3 | import example.conference.management.api.SeatTypeCreated; 4 | import example.conference.management.api.SeatTypeUpdated; 5 | import example.conference.shared.Money; 6 | import org.axonframework.domain.GenericEventMessage; 7 | import org.axonframework.eventhandling.EventBus; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.data.rest.core.annotation.HandleAfterCreate; 10 | import org.springframework.data.rest.core.annotation.HandleAfterSave; 11 | import org.springframework.data.rest.core.annotation.RepositoryEventHandler; 12 | import org.springframework.stereotype.Component; 13 | 14 | @Component 15 | @RepositoryEventHandler(SeatType.class) 16 | public class SeatTypeEventHandler { 17 | 18 | @Autowired 19 | private EventBus eventBus; 20 | 21 | @HandleAfterCreate 22 | public void publishSeatTypeCreatedEvent(SeatType seatType) { 23 | SeatTypeCreated event = new SeatTypeCreated(); 24 | 25 | event.setSeatTypeId(seatType.getId()); 26 | event.setType(seatType.getType()); 27 | event.setQuantity(seatType.getQuantity()); 28 | event.setPrice(new Money(seatType.getPriceAmount(), seatType.getPriceCurrency())); 29 | event.setConferenceId(seatType.getConference().getId()); 30 | 31 | publishEvent(event); 32 | } 33 | 34 | @HandleAfterSave 35 | public void publishSeatTypeUpdatedEvent(SeatType seatType) { 36 | SeatTypeUpdated event = new SeatTypeUpdated(); 37 | 38 | event.setSeatTypeId(seatType.getId()); 39 | event.setType(seatType.getType()); 40 | event.setQuantity(seatType.getQuantity()); 41 | event.setPrice(new Money(seatType.getPriceAmount(), seatType.getPriceCurrency())); 42 | event.setConferenceId(seatType.getConference().getId()); 43 | 44 | publishEvent(event); 45 | } 46 | 47 | private void publishEvent(Object event) { 48 | eventBus.publish(GenericEventMessage.asEventMessage(event)); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /management-impl/src/main/java/example/conference/management/impl/domain/SeatTypeRepository.java: -------------------------------------------------------------------------------- 1 | package example.conference.management.impl.domain; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @RepositoryRestResource 8 | public interface SeatTypeRepository extends JpaRepository { 9 | } 10 | -------------------------------------------------------------------------------- /management-impl/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | application: 2 | configuration: 3 | rabbit: 4 | queueName: "ManagementQueue" 5 | management: 6 | key: "value" 7 | 8 | server: 9 | port: 18080 10 | 11 | spring: 12 | datasource: 13 | driverClassName: "org.h2.Driver" 14 | url: "jdbc:h2:mem:management;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE" 15 | username: "sa" 16 | password: "" 17 | jpa: 18 | show-sql: false 19 | ddl-auto: create 20 | hibernate: 21 | naming-strategy: "org.hibernate.cfg.ImprovedNamingStrategy" 22 | jmx: 23 | enabled: false 24 | -------------------------------------------------------------------------------- /management-impl/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /payments-api/src/main/groovy/example/conference/payments/api/MakePayment.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.payments.api 2 | 3 | import example.conference.shared.Command 4 | 5 | @Command 6 | class MakePayment { 7 | String paymentId 8 | String orderId 9 | } 10 | -------------------------------------------------------------------------------- /payments-api/src/main/groovy/example/conference/payments/api/PaymentReceived.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.payments.api 2 | 3 | import example.conference.shared.Event 4 | 5 | @Event 6 | class PaymentReceived { 7 | String orderId 8 | String paymentId 9 | } 10 | -------------------------------------------------------------------------------- /payments-impl/src/main/java/example/conference/payments/impl/PaymentsApplication.java: -------------------------------------------------------------------------------- 1 | package example.conference.payments.impl; 2 | 3 | import example.conference.configuration.ConferenceConfiguration; 4 | import example.conference.payments.impl.domain.Payment; 5 | import org.axonframework.eventhandling.EventBus; 6 | import org.axonframework.eventsourcing.EventSourcingRepository; 7 | import org.axonframework.eventstore.EventStore; 8 | import org.axonframework.repository.LockManager; 9 | import org.axonframework.repository.NullLockManager; 10 | import org.springframework.boot.SpringApplication; 11 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 12 | import org.springframework.context.annotation.*; 13 | 14 | @Configuration 15 | @EnableAutoConfiguration 16 | @Import({ConferenceConfiguration.class}) 17 | @ImportResource("classpath:/payments.xml") 18 | @ComponentScan 19 | public class PaymentsApplication { 20 | 21 | public static void main(String[] args) { 22 | SpringApplication.run(PaymentsApplication.class, args); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /payments-impl/src/main/java/example/conference/payments/impl/controllers/PaymentController.java: -------------------------------------------------------------------------------- 1 | package example.conference.payments.impl.controllers; 2 | 3 | import example.conference.payments.api.MakePayment; 4 | import org.axonframework.commandhandling.gateway.CommandGateway; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.http.HttpStatus; 7 | import org.springframework.web.bind.annotation.*; 8 | 9 | @RestController 10 | public class PaymentController { 11 | 12 | @Autowired 13 | private CommandGateway commandGateway; 14 | 15 | @RequestMapping(value = "/makePayment", method = RequestMethod.POST) 16 | @ResponseStatus(HttpStatus.CREATED) 17 | public void placeOrder(@RequestBody MakePayment command) { 18 | commandGateway.send(command); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /payments-impl/src/main/java/example/conference/payments/impl/domain/Payment.java: -------------------------------------------------------------------------------- 1 | package example.conference.payments.impl.domain; 2 | 3 | import example.conference.payments.api.PaymentReceived; 4 | import org.axonframework.eventsourcing.annotation.AbstractAnnotatedAggregateRoot; 5 | import org.axonframework.eventsourcing.annotation.AggregateIdentifier; 6 | import org.axonframework.eventsourcing.annotation.EventSourcingHandler; 7 | 8 | import static java.util.Objects.requireNonNull; 9 | 10 | public class Payment extends AbstractAnnotatedAggregateRoot { 11 | 12 | @AggregateIdentifier 13 | private String paymentId; 14 | 15 | private String orderId; 16 | 17 | public Payment(String paymentId, String orderId) { 18 | requireNonNull(paymentId); 19 | requireNonNull(orderId); 20 | 21 | apply(new PaymentReceived(paymentId, orderId)); 22 | } 23 | 24 | @EventSourcingHandler 25 | private void handle(PaymentReceived event) { 26 | this.paymentId = event.getPaymentId(); 27 | this.orderId = event.getOrderId(); 28 | } 29 | 30 | protected Payment() { 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /payments-impl/src/main/java/example/conference/payments/impl/handlers/PaymentHandler.java: -------------------------------------------------------------------------------- 1 | package example.conference.payments.impl.handlers; 2 | 3 | import example.conference.payments.api.MakePayment; 4 | import example.conference.payments.impl.domain.Payment; 5 | import org.axonframework.commandhandling.annotation.CommandHandler; 6 | import org.axonframework.eventsourcing.EventSourcingRepository; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Component; 9 | 10 | @Component 11 | public class PaymentHandler { 12 | 13 | @Autowired 14 | private EventSourcingRepository repository; 15 | 16 | @CommandHandler 17 | public void handle(MakePayment command) { 18 | Payment payment = new Payment(command.getPaymentId(), command.getOrderId()); 19 | repository.add(payment); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /payments-impl/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | application: 2 | configuration: 3 | rabbit: 4 | queueName: "PaymentsQueue" 5 | payments: 6 | eventStoreBaseDir: "${java.io.tmpdir}/example-axon/payments" 7 | 8 | server: 9 | port: 38080 10 | 11 | spring: 12 | jmx: 13 | enabled: false 14 | -------------------------------------------------------------------------------- /payments-impl/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /payments-impl/src/main/resources/payments.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 11 | 12 | 14 | 15 | -------------------------------------------------------------------------------- /registrations-api/src/main/groovy/example/conference/registrations/api/AddSeats.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.registrations.api 2 | 3 | import example.conference.shared.Command 4 | 5 | @Command 6 | class AddSeats { 7 | String conferenceId 8 | String seatTypeId 9 | int quantity 10 | } 11 | -------------------------------------------------------------------------------- /registrations-api/src/main/groovy/example/conference/registrations/api/CancelSeatsReservation.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.registrations.api 2 | 3 | import example.conference.shared.Command 4 | 5 | @Command 6 | class CancelSeatsReservation { 7 | String conferenceId 8 | List seats 9 | String orderId 10 | } 11 | -------------------------------------------------------------------------------- /registrations-api/src/main/groovy/example/conference/registrations/api/ConfirmOrder.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.registrations.api 2 | 3 | import example.conference.shared.Command 4 | 5 | @Command 6 | class ConfirmOrder { 7 | String orderId 8 | } 9 | -------------------------------------------------------------------------------- /registrations-api/src/main/groovy/example/conference/registrations/api/CreateSeatsAvailability.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.registrations.api 2 | 3 | import example.conference.shared.Command 4 | 5 | @Command 6 | class CreateSeatsAvailability { 7 | String conferenceId 8 | } 9 | -------------------------------------------------------------------------------- /registrations-api/src/main/groovy/example/conference/registrations/api/MakeSeatsReservation.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.registrations.api 2 | 3 | import example.conference.shared.Command 4 | 5 | @Command 6 | class MakeSeatsReservation { 7 | String conferenceId 8 | List seats 9 | String orderId 10 | } 11 | -------------------------------------------------------------------------------- /registrations-api/src/main/groovy/example/conference/registrations/api/OrderConfirmed.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.registrations.api 2 | 3 | import example.conference.shared.Event 4 | 5 | @Event 6 | class OrderConfirmed { 7 | String orderId 8 | } 9 | -------------------------------------------------------------------------------- /registrations-api/src/main/groovy/example/conference/registrations/api/OrderPlaced.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.registrations.api 2 | 3 | import example.conference.shared.Event 4 | 5 | @Event 6 | class OrderPlaced { 7 | String orderId 8 | String conferenceId 9 | List seats 10 | int timeout 11 | } 12 | -------------------------------------------------------------------------------- /registrations-api/src/main/groovy/example/conference/registrations/api/RegisterToConference.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.registrations.api 2 | 3 | import example.conference.shared.Command 4 | 5 | @Command 6 | class RegisterToConference { 7 | String orderId 8 | String conferenceId 9 | List seats 10 | } 11 | -------------------------------------------------------------------------------- /registrations-api/src/main/groovy/example/conference/registrations/api/RegistrationExpired.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.registrations.api 2 | 3 | import example.conference.shared.Event 4 | 5 | @Event 6 | class RegistrationExpired { 7 | String orderId 8 | } 9 | -------------------------------------------------------------------------------- /registrations-api/src/main/groovy/example/conference/registrations/api/RejectOrder.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.registrations.api 2 | 3 | import example.conference.shared.Command 4 | 5 | @Command 6 | class RejectOrder { 7 | String orderId 8 | } 9 | -------------------------------------------------------------------------------- /registrations-api/src/main/groovy/example/conference/registrations/api/RemoveSeats.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.registrations.api 2 | 3 | import example.conference.shared.Command 4 | 5 | @Command 6 | class RemoveSeats { 7 | String conferenceId 8 | String seatTypeId 9 | int quantity 10 | } 11 | -------------------------------------------------------------------------------- /registrations-api/src/main/groovy/example/conference/registrations/api/SeatQuantity.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.registrations.api 2 | 3 | import example.conference.shared.ValueObject 4 | 5 | @ValueObject 6 | class SeatQuantity { 7 | String seatTypeId 8 | int quantity 9 | } 10 | -------------------------------------------------------------------------------- /registrations-api/src/main/groovy/example/conference/registrations/api/SeatsAvailabilityChanged.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.registrations.api 2 | 3 | import example.conference.shared.Event 4 | 5 | @Event 6 | class SeatsAvailabilityChanged { 7 | String conferenceId 8 | String seatTypeId 9 | int quantity 10 | } 11 | -------------------------------------------------------------------------------- /registrations-api/src/main/groovy/example/conference/registrations/api/SeatsAvailabilityCreated.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.registrations.api 2 | 3 | import example.conference.shared.Event 4 | 5 | @Event 6 | class SeatsAvailabilityCreated { 7 | String conferenceId 8 | } 9 | -------------------------------------------------------------------------------- /registrations-api/src/main/groovy/example/conference/registrations/api/SeatsReservationAccepted.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.registrations.api 2 | 3 | import example.conference.shared.Event 4 | 5 | @Event 6 | class SeatsReservationAccepted { 7 | String orderId 8 | List seats 9 | } 10 | -------------------------------------------------------------------------------- /registrations-api/src/main/groovy/example/conference/registrations/api/SeatsReservationCancelled.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.registrations.api 2 | 3 | import example.conference.shared.Event 4 | 5 | @Event 6 | class SeatsReservationCancelled { 7 | String orderId 8 | List seats 9 | } 10 | -------------------------------------------------------------------------------- /registrations-api/src/main/groovy/example/conference/registrations/api/SeatsReservationRejected.groovy: -------------------------------------------------------------------------------- 1 | package example.conference.registrations.api 2 | 3 | import example.conference.shared.Event 4 | 5 | @Event 6 | class SeatsReservationRejected { 7 | String orderId 8 | List seats 9 | List rejectionReasons 10 | } 11 | -------------------------------------------------------------------------------- /registrations-impl/src/main/java/example/conference/registrations/impl/RegistrationsApplication.java: -------------------------------------------------------------------------------- 1 | package example.conference.registrations.impl; 2 | 3 | import example.conference.configuration.ConferenceConfiguration; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 6 | import org.springframework.context.annotation.ComponentScan; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.context.annotation.Import; 9 | import org.springframework.context.annotation.ImportResource; 10 | 11 | @Configuration 12 | @EnableAutoConfiguration 13 | @Import({ConferenceConfiguration.class}) 14 | @ImportResource("classpath:/registrations.xml") 15 | @ComponentScan 16 | public class RegistrationsApplication { 17 | 18 | public static void main(String[] args) { 19 | SpringApplication.run(RegistrationsApplication.class, args); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /registrations-impl/src/main/java/example/conference/registrations/impl/controllers/RegistrationsController.java: -------------------------------------------------------------------------------- 1 | package example.conference.registrations.impl.controllers; 2 | 3 | import example.conference.registrations.api.RegisterToConference; 4 | import org.axonframework.commandhandling.gateway.CommandGateway; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.http.HttpStatus; 7 | import org.springframework.web.bind.annotation.*; 8 | 9 | @RestController 10 | public class RegistrationsController { 11 | 12 | @Autowired 13 | private CommandGateway commandGateway; 14 | 15 | @RequestMapping(value = "/registerToConference", method = RequestMethod.POST) 16 | @ResponseStatus(HttpStatus.CREATED) 17 | public void placeOrder(@RequestBody RegisterToConference command) { 18 | commandGateway.send(command); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /registrations-impl/src/main/java/example/conference/registrations/impl/domain/Order.java: -------------------------------------------------------------------------------- 1 | package example.conference.registrations.impl.domain; 2 | 3 | import example.conference.registrations.api.OrderPlaced; 4 | import example.conference.registrations.api.SeatQuantity; 5 | import org.axonframework.eventsourcing.annotation.AbstractAnnotatedAggregateRoot; 6 | import org.axonframework.eventsourcing.annotation.AggregateIdentifier; 7 | import org.axonframework.eventsourcing.annotation.EventSourcingHandler; 8 | 9 | import java.util.List; 10 | 11 | import static java.util.Objects.requireNonNull; 12 | import static java.util.stream.Collectors.toList; 13 | 14 | public class Order extends AbstractAnnotatedAggregateRoot { 15 | 16 | private static final int DEFAULT_TIMEOUT = 3; 17 | 18 | @AggregateIdentifier 19 | private String orderId; 20 | 21 | private String conferenceId; 22 | 23 | private List items; 24 | 25 | public Order(String orderId, String conferenceId, List items) { 26 | requireNonNull(orderId); 27 | requireNonNull(conferenceId); 28 | requireNonNull(items); 29 | 30 | if (items.isEmpty()) { 31 | throw new IllegalArgumentException("Expected at least one order item."); 32 | } 33 | 34 | List seats = items.stream().map(OrderItem::toSeatQuantity).collect(toList()); 35 | apply(new OrderPlaced(orderId, conferenceId, seats, DEFAULT_TIMEOUT)); 36 | } 37 | 38 | @EventSourcingHandler 39 | private void handle(OrderPlaced event) { 40 | this.orderId = event.getOrderId(); 41 | this.conferenceId = event.getConferenceId(); 42 | this.items = OrderItem.of(event.getSeats()); 43 | } 44 | 45 | protected Order() { 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /registrations-impl/src/main/java/example/conference/registrations/impl/domain/OrderItem.java: -------------------------------------------------------------------------------- 1 | package example.conference.registrations.impl.domain; 2 | 3 | import example.conference.registrations.api.SeatQuantity; 4 | 5 | import java.util.List; 6 | import java.util.Objects; 7 | 8 | import static java.util.stream.Collectors.toList; 9 | 10 | public class OrderItem { 11 | 12 | private String seatType; 13 | private int quantity; 14 | 15 | public OrderItem(String seatType, int quantity) { 16 | this.seatType = Objects.requireNonNull(seatType); 17 | if (quantity <= 0) { 18 | throw new IllegalArgumentException("Quantity must be greater than zero, but was" + quantity + "."); 19 | } 20 | 21 | this.quantity = quantity; 22 | } 23 | 24 | public static OrderItem of(SeatQuantity seat) { 25 | return new OrderItem(seat.getSeatTypeId(), seat.getQuantity()); 26 | } 27 | 28 | public static List of(List seats) { 29 | return seats.stream().map(OrderItem::of).collect(toList()); 30 | } 31 | 32 | public SeatQuantity toSeatQuantity() { 33 | return new SeatQuantity(seatType, quantity); 34 | } 35 | 36 | } 37 | 38 | -------------------------------------------------------------------------------- /registrations-impl/src/main/java/example/conference/registrations/impl/domain/RegistrationSaga.java: -------------------------------------------------------------------------------- 1 | package example.conference.registrations.impl.domain; 2 | 3 | import example.conference.payments.api.PaymentReceived; 4 | import example.conference.registrations.api.*; 5 | import org.axonframework.commandhandling.gateway.CommandGateway; 6 | import org.axonframework.eventhandling.scheduling.EventScheduler; 7 | import org.axonframework.saga.annotation.AbstractAnnotatedSaga; 8 | import org.axonframework.saga.annotation.SagaEventHandler; 9 | import org.axonframework.saga.annotation.StartSaga; 10 | import org.joda.time.Duration; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | 15 | import java.util.List; 16 | 17 | public class RegistrationSaga extends AbstractAnnotatedSaga { 18 | 19 | private static final Logger logger = LoggerFactory.getLogger(RegistrationSaga.class); 20 | 21 | @Autowired 22 | private transient CommandGateway commandGateway; 23 | 24 | @Autowired 25 | private transient EventScheduler eventScheduler; 26 | 27 | private String conferenceId; 28 | private List seats; 29 | 30 | private boolean seatsReservationAccepted = false; 31 | private boolean paymentReceived = false; 32 | 33 | 34 | @StartSaga 35 | @SagaEventHandler(associationProperty = "orderId") 36 | public void handle(OrderPlaced event) { 37 | logger.debug("A new order '{}' is placed.", event.getOrderId()); 38 | 39 | this.conferenceId = event.getConferenceId(); 40 | this.seats = event.getSeats(); 41 | 42 | MakeSeatsReservation makeSeatsReservation = new MakeSeatsReservation(conferenceId, seats, event.getOrderId()); 43 | commandGateway.send(makeSeatsReservation); 44 | 45 | RegistrationExpired orderExpired = new RegistrationExpired(event.getOrderId()); 46 | eventScheduler.schedule(Duration.standardMinutes(event.getTimeout()), orderExpired); 47 | } 48 | 49 | @SagaEventHandler(associationProperty = "orderId") 50 | public void handle(SeatsReservationAccepted event) { 51 | logger.debug("Seats reservation for order '{}' is accepted.", event.getOrderId()); 52 | 53 | seatsReservationAccepted = true; 54 | 55 | if (paymentReceived) { 56 | confirmOrder(event.getOrderId()); 57 | } 58 | } 59 | 60 | @SagaEventHandler(associationProperty = "orderId") 61 | public void handle(PaymentReceived event) { 62 | logger.debug("Payment for order '{}' is received.", event.getOrderId()); 63 | 64 | paymentReceived = true; 65 | 66 | if (seatsReservationAccepted) { 67 | confirmOrder(event.getOrderId()); 68 | } 69 | } 70 | 71 | @SagaEventHandler(associationProperty = "orderId") 72 | public void handle(SeatsReservationRejected event) { 73 | if (logger.isDebugEnabled()) { 74 | StringBuilder sb = new StringBuilder() 75 | .append("Seats reservation is rejected for order: ") 76 | .append(event.getOrderId()) 77 | .append(". Rejection reasons:"); 78 | 79 | for (String reason : event.getRejectionReasons()) { 80 | sb.append("\n\t").append(reason); 81 | } 82 | logger.debug(sb.toString()); 83 | } 84 | 85 | rejectOrder(event.getOrderId()); 86 | } 87 | 88 | @SagaEventHandler(associationProperty = "orderId") 89 | public void handle(RegistrationExpired event) { 90 | logger.debug("Order '{}' is expired.", event.getOrderId()); 91 | 92 | if (seatsReservationAccepted) { 93 | CancelSeatsReservation cancelSeatsReservation = new CancelSeatsReservation(conferenceId, seats, event.getOrderId()); 94 | commandGateway.send(cancelSeatsReservation); 95 | } 96 | 97 | if (paymentReceived) { 98 | // TODO: How to compensate payment? 99 | } 100 | 101 | rejectOrder(event.getOrderId()); 102 | } 103 | 104 | private void confirmOrder(String orderId) { 105 | ConfirmOrder confirmOrder = new ConfirmOrder(orderId); 106 | commandGateway.send(confirmOrder); 107 | end(); 108 | 109 | } 110 | 111 | private void rejectOrder(String orderId) { 112 | RejectOrder rejectOrder = new RejectOrder(orderId); 113 | commandGateway.send(rejectOrder); 114 | end(); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /registrations-impl/src/main/java/example/conference/registrations/impl/domain/SeatsAvailability.java: -------------------------------------------------------------------------------- 1 | package example.conference.registrations.impl.domain; 2 | 3 | import example.conference.registrations.api.*; 4 | import org.axonframework.eventsourcing.annotation.AbstractAnnotatedAggregateRoot; 5 | import org.axonframework.eventsourcing.annotation.AggregateIdentifier; 6 | import org.axonframework.eventsourcing.annotation.EventSourcingHandler; 7 | 8 | import java.util.ArrayList; 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | import static java.util.Objects.requireNonNull; 14 | 15 | public class SeatsAvailability extends AbstractAnnotatedAggregateRoot { 16 | 17 | @AggregateIdentifier 18 | private String conferenceId; 19 | 20 | private Map availableSeats = new HashMap<>(); 21 | 22 | public SeatsAvailability(String conferenceId) { 23 | requireNonNull(conferenceId); 24 | 25 | apply(new SeatsAvailabilityCreated(conferenceId)); 26 | } 27 | 28 | public void addSeats(String seatTypeId, int quantity) { 29 | checkNumberOfSeats(quantity); 30 | 31 | apply(new SeatsAvailabilityChanged(conferenceId, seatTypeId, quantity)); 32 | } 33 | 34 | public void removeSeats(String seatTypeId, int quantity) { 35 | checkNumberOfSeats(quantity); 36 | 37 | apply(new SeatsAvailabilityChanged(conferenceId, seatTypeId, -quantity)); 38 | } 39 | 40 | public void makeSeatsReservation(List seats, String orderId) { 41 | requireNonNull(seats); 42 | // TODO: checkNumberOfSeats(quantity); 43 | requireNonNull(orderId); 44 | 45 | List rejectionReasons = new ArrayList<>(); 46 | for (SeatQuantity seat : seats) { 47 | String seatTypeId = seat.getSeatTypeId(); 48 | int quantity = seat.getQuantity(); 49 | 50 | Integer availableNumberOfSeats = availableSeats.get(seatTypeId); 51 | if (quantity > availableNumberOfSeats) { 52 | String rejectionReason = "Insufficient number of seats: " + availableNumberOfSeats + " but requested: " + quantity + "."; 53 | rejectionReasons.add(rejectionReason); 54 | } 55 | } 56 | 57 | if (rejectionReasons.isEmpty()) { 58 | apply(new SeatsReservationAccepted(orderId, seats)); 59 | } else { 60 | apply(new SeatsReservationRejected(orderId, seats, rejectionReasons)); 61 | } 62 | } 63 | 64 | 65 | public void cancelSeatsReservation(List seats, String orderId) { 66 | requireNonNull(seats); 67 | //TODO: checkNumberOfSeats(quantity); 68 | requireNonNull(orderId); 69 | 70 | apply(new SeatsReservationCancelled(orderId, seats)); 71 | } 72 | 73 | @EventSourcingHandler 74 | private void handle(SeatsAvailabilityCreated event) { 75 | this.conferenceId = event.getConferenceId(); 76 | } 77 | 78 | @EventSourcingHandler 79 | private void handle(SeatsAvailabilityChanged event) { 80 | String seatTypeId = event.getSeatTypeId(); 81 | int quantity = event.getQuantity(); 82 | 83 | availableSeats.compute(seatTypeId, (k, v) -> v == null ? quantity : v + quantity); 84 | } 85 | 86 | @EventSourcingHandler 87 | private void handle(SeatsReservationAccepted event) { 88 | for (SeatQuantity seat : event.getSeats()) { 89 | String seatTypeId = seat.getSeatTypeId(); 90 | int quantity = seat.getQuantity(); 91 | availableSeats.compute(seatTypeId, (k, v) -> v - quantity); 92 | } 93 | } 94 | 95 | @EventSourcingHandler 96 | private void handle(SeatsReservationCancelled event) { 97 | for (SeatQuantity seat : event.getSeats()) { 98 | String seatTypeId = seat.getSeatTypeId(); 99 | int quantity = seat.getQuantity(); 100 | availableSeats.compute(seatTypeId, (k, v) -> v + quantity); 101 | } 102 | } 103 | 104 | private void checkNumberOfSeats(int numberOfSeats) { 105 | if (numberOfSeats <= 0) { 106 | throw new IllegalArgumentException("Number of seats must be grater than 0, but was: " + numberOfSeats + "."); 107 | } 108 | } 109 | 110 | protected SeatsAvailability() { 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /registrations-impl/src/main/java/example/conference/registrations/impl/handlers/OrderEventHandler.java: -------------------------------------------------------------------------------- 1 | package example.conference.registrations.impl.handlers; 2 | 3 | import example.conference.registrations.api.RegisterToConference; 4 | import example.conference.registrations.impl.domain.Order; 5 | import example.conference.registrations.impl.domain.OrderItem; 6 | import org.axonframework.commandhandling.annotation.CommandHandler; 7 | import org.axonframework.repository.Repository; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Component; 10 | 11 | @Component 12 | public class OrderEventHandler { 13 | 14 | @Autowired 15 | private Repository orderRepository; 16 | 17 | @CommandHandler 18 | public void registerToConference(RegisterToConference command) { 19 | Order order = new Order( 20 | command.getOrderId(), 21 | command.getConferenceId(), 22 | OrderItem.of(command.getSeats())); 23 | 24 | orderRepository.add(order); 25 | } 26 | 27 | 28 | 29 | 30 | } 31 | -------------------------------------------------------------------------------- /registrations-impl/src/main/java/example/conference/registrations/impl/handlers/RegistrationEventHandler.java: -------------------------------------------------------------------------------- 1 | package example.conference.registrations.impl.handlers; 2 | 3 | import example.conference.management.api.ConferenceCreated; 4 | import example.conference.management.api.SeatTypeCreated; 5 | import example.conference.management.api.SeatTypeUpdated; 6 | import example.conference.registrations.api.AddSeats; 7 | import example.conference.registrations.api.CreateSeatsAvailability; 8 | import org.axonframework.commandhandling.gateway.CommandGateway; 9 | import org.axonframework.eventhandling.annotation.EventHandler; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.stereotype.Component; 12 | 13 | @Component 14 | public class RegistrationEventHandler { 15 | 16 | @Autowired 17 | private CommandGateway commandGateway; 18 | 19 | @EventHandler 20 | public void handle(ConferenceCreated event) { 21 | CreateSeatsAvailability command = new CreateSeatsAvailability( 22 | event.getConferenceId()); 23 | 24 | commandGateway.send(command); 25 | } 26 | 27 | @EventHandler 28 | public void handle(SeatTypeCreated event) { 29 | AddSeats command = new AddSeats( 30 | event.getConferenceId(), 31 | event.getSeatTypeId(), 32 | event.getQuantity()); 33 | 34 | commandGateway.send(command); 35 | } 36 | 37 | @EventHandler 38 | public void handle(SeatTypeUpdated event) { 39 | // TODO: hot to map event into AddSeats or RemoveSeats 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /registrations-impl/src/main/java/example/conference/registrations/impl/handlers/SeatsAvailabilityHandler.java: -------------------------------------------------------------------------------- 1 | package example.conference.registrations.impl.handlers; 2 | 3 | import example.conference.registrations.api.*; 4 | import example.conference.registrations.impl.domain.SeatsAvailability; 5 | import org.axonframework.commandhandling.annotation.CommandHandler; 6 | import org.axonframework.repository.Repository; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Component; 9 | 10 | @Component 11 | public class SeatsAvailabilityHandler { 12 | 13 | @Autowired 14 | private Repository seatsAvailabilityRepository; 15 | 16 | @CommandHandler 17 | public void handle(CreateSeatsAvailability command) { 18 | SeatsAvailability seatsAvailability = new SeatsAvailability(command.getConferenceId()); 19 | seatsAvailabilityRepository.add(seatsAvailability); 20 | } 21 | 22 | @CommandHandler 23 | public void handle(AddSeats command) { 24 | SeatsAvailability seatsAvailability = seatsAvailabilityRepository.load(command.getConferenceId()); 25 | seatsAvailability.addSeats(command.getSeatTypeId(), command.getQuantity()); 26 | } 27 | 28 | @CommandHandler 29 | public void handle(RemoveSeats command) { 30 | SeatsAvailability seatsAvailability = seatsAvailabilityRepository.load(command.getConferenceId()); 31 | seatsAvailability.removeSeats(command.getSeatTypeId(), command.getQuantity()); 32 | } 33 | 34 | @CommandHandler 35 | public void handle(MakeSeatsReservation command) { 36 | SeatsAvailability seatsAvailability = seatsAvailabilityRepository.load(command.getConferenceId()); 37 | seatsAvailability.makeSeatsReservation(command.getSeats(), command.getOrderId()); 38 | } 39 | 40 | @CommandHandler 41 | public void handle(CancelSeatsReservation command) { 42 | SeatsAvailability seatsAvailability = seatsAvailabilityRepository.load(command.getConferenceId()); 43 | seatsAvailability.cancelSeatsReservation(command.getSeats(), command.getOrderId()); 44 | } 45 | 46 | 47 | } 48 | -------------------------------------------------------------------------------- /registrations-impl/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | application: 2 | configuration: 3 | rabbit: 4 | queueName: "RegistrationsQueue" 5 | registrations: 6 | eventStoreBaseDir: "${java.io.tmpdir}/example-axon/registrations" 7 | 8 | server: 9 | port: 28080 10 | 11 | spring: 12 | jmx: 13 | enabled: false 14 | -------------------------------------------------------------------------------- /registrations-impl/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /registrations-impl/src/main/resources/registrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 11 | 13 | 14 | 15 | 17 | 19 | 20 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include 'conference-client', 2 | 'conference-configuration', 3 | 'conference-shared', 4 | 'management-api', 'management-impl', 5 | 'payments-api', 'payments-impl', 6 | 'registrations-api', 'registrations-impl' 7 | 8 | 9 | --------------------------------------------------------------------------------