├── .gitignore
├── .travis.yml
├── README.md
├── build.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── pom.xml
├── settings.gradle
└── src
├── main
├── java
│ └── example
│ │ ├── bootstrap
│ │ ├── BootstrapEvent.java
│ │ └── BootstrapListener.java
│ │ ├── ddd
│ │ ├── AggregateRoot.java
│ │ ├── ApplicationService.java
│ │ ├── Event.java
│ │ ├── EventHandler.java
│ │ ├── EventPublisher.java
│ │ ├── Repository.java
│ │ └── ValueObject.java
│ │ └── scrumboard
│ │ ├── application
│ │ ├── ScrumBoardApplicationConfig.java
│ │ ├── api
│ │ │ ├── ProductService.java
│ │ │ ├── ReleaseService.java
│ │ │ ├── SprintService.java
│ │ │ ├── TaskService.java
│ │ │ └── commands
│ │ │ │ ├── CreateProductCommand.groovy
│ │ │ │ ├── CreateTaskCommand.groovy
│ │ │ │ ├── PlanBacklogItemCommand.groovy
│ │ │ │ ├── ReorderBacklogItemsCommand.groovy
│ │ │ │ ├── ScheduleReleaseCommand.groovy
│ │ │ │ └── ScheduleSprintCommand.groovy
│ │ ├── bootstrap
│ │ │ └── ScrumBoardBootstrap.groovy
│ │ ├── handlers
│ │ │ └── SampleEventHandler.java
│ │ ├── impl
│ │ │ ├── BacklogItemServiceImpl.java
│ │ │ ├── ProductServiceImpl.java
│ │ │ ├── ReleaseServiceImpl.java
│ │ │ ├── SprintServiceImpl.java
│ │ │ └── TaskServiceImpl.java
│ │ ├── system
│ │ │ ├── DateProvider.java
│ │ │ └── UserProvider.java
│ │ └── tasks
│ │ │ └── SampleTask.java
│ │ ├── config
│ │ ├── ScrumBoardConfig.java
│ │ └── ScrumBoardInitializer.java
│ │ ├── domain
│ │ ├── ScrumBoardDomainConfig.java
│ │ ├── backlogitem
│ │ │ ├── BacklogItem.java
│ │ │ ├── BacklogItemCommited.groovy
│ │ │ ├── BacklogItemFactory.java
│ │ │ ├── BacklogItemId.java
│ │ │ ├── BacklogItemRepository.java
│ │ │ ├── BacklogItemUncommited.groovy
│ │ │ ├── Priority.java
│ │ │ ├── StoryPoints.java
│ │ │ └── task
│ │ │ │ ├── DoneTaskState.java
│ │ │ │ ├── IllegalTaskStateException.java
│ │ │ │ ├── InProgressTaskState.java
│ │ │ │ ├── RemainingAmendment.java
│ │ │ │ ├── Task.java
│ │ │ │ ├── TaskFactory.java
│ │ │ │ ├── TaskId.java
│ │ │ │ ├── TaskRepository.java
│ │ │ │ ├── TaskState.java
│ │ │ │ ├── TaskStateAdapter.java
│ │ │ │ ├── TaskStatus.java
│ │ │ │ └── TodoTaskState.java
│ │ ├── product
│ │ │ ├── BacklogItemPlannedEvent.groovy
│ │ │ ├── Product.java
│ │ │ ├── ProductBacklogItem.java
│ │ │ ├── ProductFactory.java
│ │ │ ├── ProductId.java
│ │ │ └── ProductRepository.java
│ │ ├── release
│ │ │ ├── Release.java
│ │ │ ├── ReleaseFactory.java
│ │ │ ├── ReleaseId.java
│ │ │ ├── ReleaseRepository.java
│ │ │ └── ScheduledBacklogItem.java
│ │ └── sprint
│ │ │ ├── CommitedBacklogItem.java
│ │ │ ├── Sprint.java
│ │ │ ├── SprintFactory.java
│ │ │ ├── SprintId.java
│ │ │ └── SprintRepository.java
│ │ ├── infrastructure
│ │ ├── bootstrap
│ │ │ ├── BootstrapEventPublisher.java
│ │ │ └── ScrumBoardInfrastructureBootstrapConfig.java
│ │ ├── events
│ │ │ ├── ScrumBoardInfrastructureEventsConfig.java
│ │ │ └── SpringIntegrationEventPublisher.java
│ │ ├── jpa
│ │ │ ├── ScrumBoardInfrastructureJpaConfig.java
│ │ │ ├── hibernate
│ │ │ │ └── FixedPrefixNamingStrategy.java
│ │ │ ├── repositories
│ │ │ │ ├── GenericJpaRepository.java
│ │ │ │ ├── JpaBacklogItemRepository.java
│ │ │ │ ├── JpaProductRepository.java
│ │ │ │ ├── JpaReleaseRepository.java
│ │ │ │ ├── JpaRepository.java
│ │ │ │ ├── JpaSprintRepository.java
│ │ │ │ └── JpaTaskRepository.java
│ │ │ └── spring
│ │ │ │ ├── EventsPublishingAspect.java
│ │ │ │ └── RepositoryAutowiringAspect.java
│ │ ├── rest
│ │ │ ├── ProductIdConverter.java
│ │ │ └── ScrumBoardInfrastructureRestConfig.java
│ │ └── shared
│ │ │ ├── ScrumBoardInfrastructureAsyncConfig.java
│ │ │ ├── ScrumBoardInfrastructureContextConfig.java
│ │ │ ├── ScrumBoardInfrastructureTaskConfig.java
│ │ │ └── ScrumBoardInfrastructureTransactionConfig.java
│ │ └── rest
│ │ ├── commands
│ │ ├── ScrumBoardRestCommandsConfig.java
│ │ └── product
│ │ │ └── ProductCommandController.java
│ │ └── queries
│ │ ├── ScrumBoardRestQueriesConfig.java
│ │ └── product
│ │ ├── ProductQueryController.groovy
│ │ └── dtos
│ │ ├── ProductBacklogItemDto.groovy
│ │ └── ProductDto.groovy
├── resources
│ ├── local.properties
│ ├── logback.xml
│ └── remote.properties
└── webapp
│ └── index.html
└── test
├── java
└── example
│ ├── ddd
│ └── domain
│ │ ├── AggregateRootAssert.java
│ │ └── DddAssertions.java
│ └── scrumboard
│ ├── TestGroups.java
│ ├── domain
│ ├── ScrumBoardBuilders.java
│ ├── backlogitem
│ │ ├── BacklogItemAssert.java
│ │ ├── BacklogItemBuilder.java
│ │ └── BacklogItemTest.java
│ ├── product
│ │ ├── ProductAssert.java
│ │ ├── ProductBuilder.java
│ │ └── ProductTest.java
│ └── sprint
│ │ └── SprintBuilder.java
│ ├── infrastructure
│ └── jpa
│ │ ├── JpaTest.java
│ │ ├── JpaTestConfig.java
│ │ └── repositories
│ │ ├── AbstractJpaRepositoryTest.java
│ │ └── JpaProductRepositoryTest.java
│ └── rest
│ ├── AbstractControllerTest.java
│ ├── commands
│ ├── RestCommandTest.java
│ ├── RestCommandTestConfig.java
│ └── product
│ │ └── ProductCommandControllerTest.java
│ └── queries
│ ├── RestQueryTest.java
│ ├── RestQueryTestConfig.java
│ └── product
│ └── ProductQueryControllerTest.java
└── resources
├── logback-test.xml
├── sample.sql
└── test.properties
/.gitignore:
--------------------------------------------------------------------------------
1 | # Maven
2 | /target
3 |
4 | # Gradle
5 | .gradle
6 | /build
7 |
8 | # TestNG
9 | /test-output
10 |
11 | # STS
12 | .classpath
13 | .groovy
14 | .project
15 | .settings
16 | .springBeans
17 |
18 | # IntelliJ
19 | .idea
20 | *.iml
21 | *.iws
22 | *.ipr
23 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language:
2 | - java
3 |
4 | jdk:
5 | - openjdk7
6 |
7 | branches:
8 | only:
9 | - master
10 |
11 | before_install: true
12 | install: true
13 |
14 | before_script: true
15 | script: mvn --quiet test
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/mkuthan/example-ddd-cqrs-server)
2 |
3 | [Presentation](https://docs.google.com/presentation/d/1PlKF4OW5ARqUbqSUwL4D1syEwxw-PmX4KLJObPeYQyI/pub?start=false&loop=false&delayms=3000)
4 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'groovy'
2 | apply plugin: 'idea'
3 | apply plugin: 'jetty'
4 | apply plugin: 'war'
5 |
6 | repositories {
7 | mavenCentral()
8 | maven { url "http://maven.springframework.org/milestone/" }
9 | }
10 |
11 | sourceSets {
12 | main {
13 | java {
14 | srcDirs = []
15 | }
16 | groovy {
17 | srcDirs = ['src/java']
18 | }
19 | }
20 |
21 | test {
22 | java {
23 | srcDirs = []
24 | }
25 | groovy {
26 | srcDirs = ['test/java']
27 | }
28 | }
29 | }
30 |
31 | idea {
32 | project {
33 | jdkName = '1.7'
34 | languageLevel = '1.7'
35 | }
36 | }
37 |
38 | ext {
39 | springVersion = "4.0.3.RELEASE"
40 | }
41 |
42 | configurations.all {
43 | exclude group: 'commons-logging'
44 | }
45 |
46 | dependencies {
47 | providedCompile 'javax.servlet:javax.servlet-api:3.0.1'
48 |
49 | compile "org.codehaus.groovy:groovy-all:2.1.8"
50 |
51 | compile "org.springframework:spring-aop:$springVersion"
52 | compile "org.springframework:spring-aspects:$springVersion"
53 | compile "org.springframework:spring-context:$springVersion"
54 | compile "org.springframework:spring-context-support:$springVersion"
55 | compile "org.springframework:spring-orm:$springVersion"
56 | testCompile "org.springframework:spring-test:$springVersion"
57 | compile "org.springframework:spring-web:$springVersion"
58 | compile "org.springframework:spring-webmvc:$springVersion"
59 | compile 'org.springframework.data:spring-data-commons:1.7.1.RELEASE'
60 | compile "org.springframework.integration:spring-integration-core:4.0.0.RC1"
61 |
62 | compile "org.hibernate:hibernate-entitymanager:4.3.5.Final"
63 |
64 | compile 'com.h2database:h2:1.4.177'
65 |
66 | compile 'org.slf4j:slf4j-api:1.7.7'
67 | runtime 'org.slf4j:jcl-over-slf4j:1.7.7'
68 | runtime 'org.slf4j:jul-to-slf4j:1.7.7'
69 | runtime 'ch.qos.logback:logback-classic:1.1.2'
70 |
71 |
72 | compile 'org.apache.commons:commons-lang3:3.3.2'
73 | compile 'com.google.guava:guava:16.0.1'
74 | compile 'joda-time:joda-time:2.3'
75 | compile 'com.fasterxml.jackson.core:jackson-databind:2.3.3'
76 |
77 | testCompile("org.testng:testng:6.8.8") {
78 | exclude(module: 'junit')
79 | exclude(module: 'bsh')
80 | exclude(module: 'snakeyaml')
81 | }
82 |
83 | testCompile "org.mockito:mockito-core:1.9.5"
84 | testCompile "org.assertj:assertj-core:1.6.0"
85 | testCompile "com.googlecode.catch-exception:catch-exception:1.2.0"
86 | testCompile "com.jayway.jsonpath:json-path:0.9.1"
87 | }
88 |
89 | task wrapper(type: Wrapper) {
90 | gradleVersion = '1.11'
91 | }
92 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkuthan/example-ddd-cqrs-server/3f4b0136bf816bd5d358c6cc3dfd9501fac78084/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Apr 23 20:52:25 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-bin.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 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | example
5 | example-ddd-cqrs-server
6 | 1.0-SNAPSHOT
7 | war
8 |
9 |
10 | UTF-8
11 |
12 | 1.7
13 | 1.7
14 |
15 | false
16 |
17 | 3.3.2
18 | 16.0.1
19 | 1.4.177
20 | 4.3.5.Final
21 | 2.3.3
22 | 2.3
23 | 1.1.2
24 | 1.7.7
25 | 4.0.3.RELEASE
26 | 4.0.0.RC1
27 | 1.7.1.RELEASE
28 |
29 | 2.1.8
30 | 2.8.0-01
31 | 2.1.8-01
32 |
33 | 1.6.0
34 | 1.2.0
35 | 0.9.1
36 | 1.9.5
37 | 6.8.8
38 |
39 |
40 |
41 |
42 | spring-milestone
43 | http://maven.springframework.org/milestone/
44 |
45 |
46 |
47 |
48 | 3.0.5
49 |
50 |
51 |
52 |
53 |
54 |
55 | maven-compiler-plugin
56 | 3.1
57 |
58 | groovy-eclipse-compiler
59 |
60 |
61 |
62 | org.codehaus.groovy
63 | groovy-eclipse-compiler
64 | ${groovy-eclipse-compiler.version}
65 |
66 |
67 | org.codehaus.groovy
68 | groovy-eclipse-batch
69 | ${groovy-eclipse-batch.version}
70 |
71 |
72 |
73 |
74 |
75 | maven-surefire-plugin
76 | 2.17
77 |
78 |
79 | default-test
80 | test
81 |
82 | test
83 |
84 |
85 | unit
86 | -Xmx64m
87 | classes
88 |
89 |
90 |
91 | integration-test
92 | test
93 |
94 | test
95 |
96 |
97 | integration
98 | -Xmx256m
99 |
100 |
101 |
102 |
103 |
104 |
105 | org.zeroturnaround
106 | jrebel-maven-plugin
107 | 1.1.5
108 |
109 |
110 | generate-rebel-xml
111 | process-resources
112 |
113 | generate
114 |
115 |
116 |
117 |
118 |
119 |
120 | org.apache.tomcat.maven
121 | tomcat7-maven-plugin
122 | 2.2
123 |
124 |
125 | org.apache.juli.ClassLoaderLogManager
126 | JNDI
127 | local
128 |
129 |
130 |
131 |
132 |
133 | org.slf4j
134 | jcl-over-slf4j
135 | ${slf4j.version}
136 |
137 |
138 | org.slf4j
139 | slf4j-api
140 | ${slf4j.version}
141 |
142 |
143 | org.slf4j
144 | jul-to-slf4j
145 | ${slf4j.version}
146 |
147 |
148 | ch.qos.logback
149 | logback-classic
150 | ${logback.version}
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 | org.zeroturnaround
160 | jrebel-maven-plugin
161 |
162 |
163 |
164 | org.apache.tomcat.maven
165 | tomcat7-maven-plugin
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 | org.springframework
174 | spring-framework-bom
175 | ${spring.version}
176 | pom
177 | import
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 | javax.servlet
186 | javax.servlet-api
187 | 3.0.1
188 | provided
189 |
190 |
191 |
192 |
193 | org.springframework
194 | spring-aop
195 |
196 |
197 | org.springframework
198 | spring-aspects
199 |
200 |
201 | org.springframework
202 | spring-context
203 |
204 |
205 | org.springframework
206 | spring-context-support
207 |
208 |
209 | org.springframework
210 | spring-core
211 |
212 |
213 | commons-logging
214 | commons-logging
215 |
216 |
217 |
218 |
219 | org.springframework
220 | spring-jms
221 |
222 |
223 | org.springframework
224 | spring-orm
225 |
226 |
227 | org.springframework
228 | spring-web
229 |
230 |
231 | org.springframework
232 | spring-webmvc
233 |
234 |
235 |
236 |
237 | org.springframework.integration
238 | spring-integration-core
239 | ${spring-integration.version}
240 |
241 |
242 |
243 |
244 | org.springframework.data
245 | spring-data-commons
246 | ${spring-data-commons.version}
247 |
248 |
249 |
250 |
251 | org.hibernate
252 | hibernate-entitymanager
253 | ${hibernate.version}
254 |
255 |
256 | xml-apis
257 | xml-apis
258 |
259 |
260 |
261 |
262 |
263 |
264 | org.codehaus.groovy
265 | groovy-all
266 | ${groovy.version}
267 |
268 |
269 |
270 |
271 | org.slf4j
272 | slf4j-api
273 | ${slf4j.version}
274 |
275 |
276 |
277 | org.slf4j
278 | jcl-over-slf4j
279 | ${slf4j.version}
280 | runtime
281 |
282 |
283 |
284 | org.slf4j
285 | jul-to-slf4j
286 | ${slf4j.version}
287 | runtime
288 |
289 |
290 |
291 | ch.qos.logback
292 | logback-classic
293 | ${logback.version}
294 | runtime
295 |
296 |
297 |
298 |
299 | org.apache.commons
300 | commons-lang3
301 | ${commons-lang.version}
302 |
303 |
304 |
305 | com.google.guava
306 | guava
307 | ${guava.version}
308 |
309 |
310 |
311 | joda-time
312 | joda-time
313 | ${jodatime.version}
314 |
315 |
316 |
317 | com.fasterxml.jackson.core
318 | jackson-databind
319 | ${jackson.version}
320 |
321 |
322 |
323 |
324 | com.h2database
325 | h2
326 | ${h2.version}
327 |
328 |
329 |
330 |
331 | org.springframework
332 | spring-test
333 | test
334 |
335 |
336 |
337 | org.testng
338 | testng
339 | ${testng.version}
340 | test
341 |
342 |
343 | junit
344 | junit
345 |
346 |
347 | bsh
348 | org.beanshell
349 |
350 |
351 | snakeyaml
352 | org.yaml
353 |
354 |
355 |
356 |
357 |
358 | org.mockito
359 | mockito-core
360 | ${mockito.version}
361 | test
362 |
363 |
364 |
365 | org.assertj
366 | assertj-core
367 | ${assertj.version}
368 | test
369 |
370 |
371 |
372 | com.googlecode.catch-exception
373 | catch-exception
374 | ${catchexception.version}
375 | test
376 |
377 |
378 |
379 | com.jayway.jsonpath
380 | json-path
381 | ${jsonpath.version}
382 | test
383 |
384 |
385 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'example-ddd-cqrs-server'
2 |
--------------------------------------------------------------------------------
/src/main/java/example/bootstrap/BootstrapEvent.java:
--------------------------------------------------------------------------------
1 | package example.bootstrap;
2 |
3 | import org.springframework.context.ApplicationContext;
4 | import org.springframework.context.event.ApplicationContextEvent;
5 |
6 | public class BootstrapEvent extends ApplicationContextEvent {
7 |
8 | private static final long serialVersionUID = 1L;
9 |
10 | public BootstrapEvent(ApplicationContext source) {
11 | super(source);
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/example/bootstrap/BootstrapListener.java:
--------------------------------------------------------------------------------
1 | package example.bootstrap;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | import org.springframework.stereotype.Component;
9 |
10 | @Component
11 | @Target({ ElementType.TYPE, ElementType.METHOD })
12 | @Retention(RetentionPolicy.RUNTIME)
13 | public @interface BootstrapListener {
14 | }
--------------------------------------------------------------------------------
/src/main/java/example/ddd/AggregateRoot.java:
--------------------------------------------------------------------------------
1 | package example.ddd;
2 |
3 | import static java.util.Objects.requireNonNull;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 | import java.util.Objects;
8 |
9 | import javax.persistence.Embedded;
10 | import javax.persistence.Id;
11 | import javax.persistence.MappedSuperclass;
12 | import javax.persistence.Transient;
13 | import javax.persistence.Version;
14 |
15 | @MappedSuperclass
16 | public abstract class AggregateRoot {
17 |
18 | @Id
19 | @Embedded
20 | private ID id;
21 |
22 | @Version
23 | private Integer version;
24 |
25 | @Transient
26 | private List pendingEvents = new ArrayList<>();
27 |
28 | protected AggregateRoot() {
29 | }
30 |
31 | protected AggregateRoot(ID id) {
32 | this.id = requireNonNull(id);
33 | this.version = 0;
34 | }
35 |
36 | public ID getId() {
37 | return id;
38 | }
39 |
40 | public Integer getVersion() {
41 | return version;
42 | }
43 |
44 | public Iterable getPendingEvents() {
45 | return pendingEvents;
46 | }
47 |
48 | @Override
49 | public int hashCode() {
50 | return Objects.hash(id);
51 | }
52 |
53 | @Override
54 | public boolean equals(Object obj) {
55 | if (this == obj) {
56 | return true;
57 | }
58 |
59 | if (obj == null) {
60 | return false;
61 | }
62 |
63 | if (getClass() != obj.getClass()) {
64 | return false;
65 | }
66 |
67 | @SuppressWarnings("unchecked")
68 | AggregateRoot that = (AggregateRoot) obj;
69 |
70 | return Objects.equals(this.id, that.id);
71 | }
72 |
73 | protected void register(Event event) {
74 | requireNonNull(event);
75 | pendingEvents.add(event);
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/java/example/ddd/ApplicationService.java:
--------------------------------------------------------------------------------
1 | package example.ddd;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | import org.springframework.stereotype.Component;
9 | import org.springframework.transaction.annotation.Propagation;
10 | import org.springframework.transaction.annotation.Transactional;
11 |
12 | @Component
13 | @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
14 | @Target({ ElementType.TYPE, ElementType.METHOD })
15 | @Retention(RetentionPolicy.RUNTIME)
16 | public @interface ApplicationService {
17 | }
--------------------------------------------------------------------------------
/src/main/java/example/ddd/Event.java:
--------------------------------------------------------------------------------
1 | package example.ddd;
2 |
3 | import java.io.Serializable;
4 |
5 | public interface Event extends Serializable {
6 | }
--------------------------------------------------------------------------------
/src/main/java/example/ddd/EventHandler.java:
--------------------------------------------------------------------------------
1 | package example.ddd;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | import org.springframework.integration.annotation.ServiceActivator;
9 |
10 | @Target({ ElementType.METHOD })
11 | @Retention(RetentionPolicy.RUNTIME)
12 | @ServiceActivator(inputChannel = "eventBus")
13 | public @interface EventHandler {
14 | }
--------------------------------------------------------------------------------
/src/main/java/example/ddd/EventPublisher.java:
--------------------------------------------------------------------------------
1 | package example.ddd;
2 |
3 | public interface EventPublisher {
4 |
5 | void publish(Event event);
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/example/ddd/Repository.java:
--------------------------------------------------------------------------------
1 | package example.ddd;
2 |
3 | public interface Repository, K> {
4 |
5 | E load(K entityId);
6 |
7 | void save(E entity);
8 |
9 | void delete(E entity);
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/example/ddd/ValueObject.java:
--------------------------------------------------------------------------------
1 | package example.ddd;
2 |
3 | import java.io.Serializable;
4 |
5 | import org.apache.commons.lang3.builder.EqualsBuilder;
6 | import org.apache.commons.lang3.builder.HashCodeBuilder;
7 | import org.apache.commons.lang3.builder.ToStringBuilder;
8 | import org.apache.commons.lang3.builder.ToStringStyle;
9 |
10 | public abstract class ValueObject implements Serializable {
11 |
12 | private static final long serialVersionUID = 1L;
13 |
14 | @Override
15 | public boolean equals(Object that) {
16 | return EqualsBuilder.reflectionEquals(this, that);
17 | }
18 |
19 | @Override
20 | public int hashCode() {
21 | return HashCodeBuilder.reflectionHashCode(this);
22 | }
23 |
24 | @Override
25 | public String toString() {
26 | return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/application/ScrumBoardApplicationConfig.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.application;
2 |
3 | import org.springframework.context.annotation.ComponentScan;
4 | import org.springframework.context.annotation.Configuration;
5 |
6 | @Configuration
7 | @ComponentScan
8 | public class ScrumBoardApplicationConfig {
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/application/api/ProductService.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.application.api;
2 |
3 | import example.scrumboard.application.api.commands.CreateProductCommand;
4 | import example.scrumboard.application.api.commands.PlanBacklogItemCommand;
5 | import example.scrumboard.application.api.commands.ReorderBacklogItemsCommand;
6 | import example.scrumboard.application.api.commands.ScheduleReleaseCommand;
7 | import example.scrumboard.application.api.commands.ScheduleSprintCommand;
8 | import example.scrumboard.domain.backlogitem.BacklogItemId;
9 | import example.scrumboard.domain.product.ProductId;
10 | import example.scrumboard.domain.release.ReleaseId;
11 | import example.scrumboard.domain.sprint.SprintId;
12 |
13 | public interface ProductService {
14 |
15 | ProductId createProduct(CreateProductCommand command);
16 |
17 | BacklogItemId planBacklogItem(ProductId productId, PlanBacklogItemCommand command);
18 |
19 | void reorderBacklogItems(ProductId productId, ReorderBacklogItemsCommand command);
20 |
21 | ReleaseId scheduleRelease(ScheduleReleaseCommand command);
22 |
23 | SprintId scheduleSprint(ScheduleSprintCommand command);
24 |
25 | }
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/application/api/ReleaseService.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.application.api;
2 |
3 | import example.scrumboard.domain.backlogitem.BacklogItemId;
4 | import example.scrumboard.domain.release.ReleaseId;
5 |
6 | public interface ReleaseService {
7 |
8 | void scheduleBacklogItem(ReleaseId releaseId, BacklogItemId backlogItemId);
9 |
10 | void unscheduleBacklogItem(ReleaseId releaseId, BacklogItemId backlogItemId);
11 |
12 | void archive(ReleaseId releaseId);
13 |
14 | }
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/application/api/SprintService.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.application.api;
2 |
3 | import example.scrumboard.domain.backlogitem.BacklogItemId;
4 | import example.scrumboard.domain.sprint.SprintId;
5 |
6 | public interface SprintService {
7 |
8 | void commitBacklogItem(SprintId sprintId, BacklogItemId backlogItemId);
9 |
10 | void uncommitBacklogItem(SprintId sprintId, BacklogItemId backlogItemId);
11 |
12 | void beginSprint(SprintId sprintId);
13 |
14 | void finishSprint(SprintId sprintId);
15 |
16 | void captureRetrospective(SprintId sprintId, String retrospective);
17 | }
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/application/api/TaskService.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.application.api;
2 |
3 | import example.scrumboard.application.api.commands.CreateTaskCommand;
4 | import example.scrumboard.domain.backlogitem.task.TaskId;
5 |
6 | public interface TaskService {
7 |
8 | TaskId createTask(CreateTaskCommand command);
9 |
10 | void beginTask(TaskId taskId);
11 |
12 | void finishTask(TaskId taskId);
13 |
14 | void amendHoursRemaining(TaskId taskId, Integer hoursRemaing);
15 |
16 | }
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/application/api/commands/CreateProductCommand.groovy:
--------------------------------------------------------------------------------
1 | package example.scrumboard.application.api.commands
2 |
3 | import groovy.transform.Canonical
4 |
5 |
6 | @Canonical
7 | class CreateProductCommand {
8 | String productName
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/application/api/commands/CreateTaskCommand.groovy:
--------------------------------------------------------------------------------
1 | package example.scrumboard.application.api.commands
2 |
3 | import example.scrumboard.domain.backlogitem.BacklogItemId
4 | import groovy.transform.Canonical
5 |
6 |
7 | @Canonical
8 | class CreateTaskCommand {
9 | BacklogItemId backlogItemId
10 | String taskName
11 | String taskDescription
12 | Integer hoursRemaining
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/application/api/commands/PlanBacklogItemCommand.groovy:
--------------------------------------------------------------------------------
1 | package example.scrumboard.application.api.commands
2 |
3 | import groovy.transform.Canonical
4 |
5 |
6 | @Canonical
7 | class PlanBacklogItemCommand {
8 | String backlogItemStory
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/application/api/commands/ReorderBacklogItemsCommand.groovy:
--------------------------------------------------------------------------------
1 | package example.scrumboard.application.api.commands
2 |
3 | import example.scrumboard.domain.backlogitem.BacklogItemId
4 | import groovy.transform.Canonical
5 |
6 |
7 | @Canonical
8 | class ReorderBacklogItemsCommand {
9 | BacklogItemId[] backlogItemIds
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/application/api/commands/ScheduleReleaseCommand.groovy:
--------------------------------------------------------------------------------
1 | package example.scrumboard.application.api.commands
2 |
3 | import example.scrumboard.domain.product.ProductId
4 | import groovy.transform.Canonical
5 |
6 |
7 | @Canonical
8 | class ScheduleReleaseCommand {
9 | ProductId productId
10 | String releaseName
11 | Date releaseDate
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/application/api/commands/ScheduleSprintCommand.groovy:
--------------------------------------------------------------------------------
1 | package example.scrumboard.application.api.commands
2 |
3 | import example.scrumboard.domain.product.ProductId
4 | import groovy.transform.Canonical
5 |
6 |
7 | @Canonical
8 | class ScheduleSprintCommand {
9 | ProductId productId
10 | String sprintName
11 | Date sprintBeginDate
12 | Date sprintEndDate
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/application/bootstrap/ScrumBoardBootstrap.groovy:
--------------------------------------------------------------------------------
1 | package example.scrumboard.application.bootstrap
2 |
3 | import org.joda.time.format.DateTimeFormat
4 | import org.joda.time.format.DateTimeFormatter
5 | import org.springframework.beans.factory.annotation.Autowired
6 | import org.springframework.context.ApplicationListener
7 |
8 | import example.bootstrap.BootstrapEvent
9 | import example.bootstrap.BootstrapListener
10 | import example.scrumboard.application.api.ProductService
11 | import example.scrumboard.application.api.commands.CreateProductCommand
12 | import example.scrumboard.application.api.commands.PlanBacklogItemCommand
13 | import example.scrumboard.application.api.commands.ScheduleReleaseCommand
14 | import example.scrumboard.application.api.commands.ScheduleSprintCommand
15 | import example.scrumboard.domain.product.ProductId
16 | import groovy.sql.Sql
17 |
18 | @BootstrapListener
19 | class ScrumBoardBootstrap implements ApplicationListener {
20 |
21 | static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormat.forPattern("YYYY-MM-DD")
22 |
23 | @Autowired
24 | ProductService productService
25 |
26 | @Autowired
27 | Sql sql
28 |
29 | @Override
30 | void onApplicationEvent(BootstrapEvent event) {
31 | if (sql.firstRow("SELECT count(*) count FROM t_product").count > 0) {
32 | return
33 | }
34 |
35 | /*
36 | * Product1
37 | */
38 | ProductId productId1 = productService.createProduct(
39 | new CreateProductCommand(productName: "Example DDD/CQRS server")
40 | )
41 |
42 | // backlog items
43 | productService.planBacklogItem(productId1,
44 | new PlanBacklogItemCommand(backlogItemStory: "Write documentation")
45 | )
46 | productService.planBacklogItem(productId1,
47 | new PlanBacklogItemCommand(backlogItemStory: "Add more unit tests")
48 | )
49 |
50 | // sprints
51 | productService.scheduleSprint(
52 | new ScheduleSprintCommand(productId: productId1, sprintName: "Sprint 1", sprintBeginDate: toDate("2011-01-01"), sprintEndDate: toDate("2011-01-14"))
53 | )
54 | productService.scheduleSprint(
55 | new ScheduleSprintCommand(productId: productId1, sprintName: "Sprint 2", sprintBeginDate: toDate("2011-01-15"), sprintEndDate: toDate("2011-01-29"))
56 | )
57 |
58 | // releases
59 | productService.scheduleRelease(
60 | new ScheduleReleaseCommand(productId: productId1, releaseName: "Release 1", releaseDate: toDate("2011-01-14"))
61 | )
62 |
63 | productService.scheduleRelease(
64 | new ScheduleReleaseCommand(productId: productId1, releaseName: "Release 2", releaseDate: toDate("2011-01-29"))
65 | )
66 |
67 |
68 | /*
69 | * Product2
70 | */
71 | ProductId productId2 = productService.createProduct(
72 | new CreateProductCommand(productName: "Example DDD/CQRS client")
73 | )
74 |
75 | // backlog items
76 | productService.planBacklogItem(productId2,
77 | new PlanBacklogItemCommand(backlogItemStory: "Apply Twitter Bootstrap")
78 | )
79 | // backlog items
80 | productService.planBacklogItem(productId2,
81 | new PlanBacklogItemCommand(backlogItemStory: "Create Angular controllers")
82 | )
83 |
84 | // no sprints
85 |
86 | /*
87 | * Product3
88 | */
89 | productService.createProduct(
90 | new CreateProductCommand(productName: "Product with no backlog items")
91 | )
92 |
93 | // no backlog items
94 |
95 | // no sprints
96 | }
97 |
98 | Date toDate(String date) {
99 | DATE_TIME_FORMATTER.parseDateTime(date).toDate()
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/application/handlers/SampleEventHandler.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.application.handlers;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 | import org.springframework.integration.annotation.MessageEndpoint;
6 |
7 | import example.ddd.Event;
8 | import example.ddd.EventHandler;
9 |
10 | @MessageEndpoint
11 | public class SampleEventHandler {
12 |
13 | private static final Logger LOGGER = LoggerFactory.getLogger(SampleEventHandler.class);
14 |
15 | @EventHandler
16 | public void log(Event event) {
17 | LOGGER.info("Event handled " + event);
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/application/impl/BacklogItemServiceImpl.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.application.impl;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 |
5 | import example.ddd.ApplicationService;
6 | import example.scrumboard.domain.backlogitem.BacklogItem;
7 | import example.scrumboard.domain.backlogitem.BacklogItemFactory;
8 | import example.scrumboard.domain.backlogitem.BacklogItemId;
9 | import example.scrumboard.domain.backlogitem.Priority;
10 | import example.scrumboard.domain.backlogitem.BacklogItemRepository;
11 | import example.scrumboard.domain.backlogitem.StoryPoints;
12 |
13 | @ApplicationService
14 | public class BacklogItemServiceImpl {
15 |
16 | @Autowired
17 | private BacklogItemFactory backlogItemFactory;
18 |
19 | @Autowired
20 | private BacklogItemRepository backlogItemRepository;
21 |
22 | public void assignStoryPoints(BacklogItemId backlogItemId, StoryPoints storyPoints) {
23 | BacklogItem backlogItem = backlogItemRepository.load(backlogItemId);
24 |
25 | backlogItem.assignStoryPoints(storyPoints);
26 |
27 | backlogItemRepository.save(backlogItem);
28 | }
29 |
30 | public void assignPriority(BacklogItemId backlogItemId, Priority priority) {
31 | BacklogItem backlogItem = backlogItemRepository.load(backlogItemId);
32 |
33 | backlogItem.assignPriority(priority);
34 |
35 | backlogItemRepository.save(backlogItem);
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/application/impl/ProductServiceImpl.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.application.impl;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 |
5 | import example.ddd.ApplicationService;
6 | import example.scrumboard.application.api.ProductService;
7 | import example.scrumboard.application.api.commands.CreateProductCommand;
8 | import example.scrumboard.application.api.commands.PlanBacklogItemCommand;
9 | import example.scrumboard.application.api.commands.ReorderBacklogItemsCommand;
10 | import example.scrumboard.application.api.commands.ScheduleReleaseCommand;
11 | import example.scrumboard.application.api.commands.ScheduleSprintCommand;
12 | import example.scrumboard.domain.backlogitem.BacklogItem;
13 | import example.scrumboard.domain.backlogitem.BacklogItemFactory;
14 | import example.scrumboard.domain.backlogitem.BacklogItemId;
15 | import example.scrumboard.domain.backlogitem.BacklogItemRepository;
16 | import example.scrumboard.domain.product.Product;
17 | import example.scrumboard.domain.product.ProductFactory;
18 | import example.scrumboard.domain.product.ProductId;
19 | import example.scrumboard.domain.product.ProductRepository;
20 | import example.scrumboard.domain.release.Release;
21 | import example.scrumboard.domain.release.ReleaseFactory;
22 | import example.scrumboard.domain.release.ReleaseId;
23 | import example.scrumboard.domain.release.ReleaseRepository;
24 | import example.scrumboard.domain.sprint.Sprint;
25 | import example.scrumboard.domain.sprint.SprintFactory;
26 | import example.scrumboard.domain.sprint.SprintId;
27 | import example.scrumboard.domain.sprint.SprintRepository;
28 |
29 | @ApplicationService
30 | public class ProductServiceImpl implements ProductService {
31 |
32 | @Autowired
33 | private ProductFactory productFactory;
34 |
35 | @Autowired
36 | private ProductRepository productRepository;
37 |
38 | @Autowired
39 | private BacklogItemFactory backlogItemFactory;
40 |
41 | @Autowired
42 | private BacklogItemRepository backlogItemRepository;
43 |
44 | @Autowired
45 | private ReleaseFactory releaseFactory;
46 |
47 | @Autowired
48 | private ReleaseRepository releaseRepository;
49 |
50 | @Autowired
51 | private SprintFactory sprintFactory;
52 |
53 | @Autowired
54 | private SprintRepository sprintRepository;
55 |
56 | @Override
57 | public ProductId createProduct(CreateProductCommand command) {
58 | Product product = productFactory.create(command.getProductName());
59 | productRepository.save(product);
60 |
61 | return product.getId();
62 | }
63 |
64 | @Override
65 | public BacklogItemId planBacklogItem(ProductId productId, PlanBacklogItemCommand command) {
66 | Product product = productRepository.load(productId);
67 |
68 | BacklogItem backlogItem = backlogItemFactory.create(product, command.getBacklogItemStory());
69 | backlogItemRepository.save(backlogItem);
70 |
71 | product.planBacklogItem(backlogItem);
72 | productRepository.save(product);
73 |
74 | return backlogItem.getId();
75 | }
76 |
77 | @Override
78 | public void reorderBacklogItems(ProductId productId, ReorderBacklogItemsCommand command) {
79 | Product product = productRepository.load(productId);
80 | product.reorderBacklogItems(command.getBacklogItemIds());
81 |
82 | productRepository.save(product);
83 | }
84 |
85 | @Override
86 | public ReleaseId scheduleRelease(ScheduleReleaseCommand command) {
87 | Product product = productRepository.load(command.getProductId());
88 |
89 | Release release = releaseFactory.create(product, command.getReleaseName(), command.getReleaseDate());
90 | releaseRepository.save(release);
91 |
92 | return release.getId();
93 | }
94 |
95 | @Override
96 | public SprintId scheduleSprint(ScheduleSprintCommand command) {
97 | Product product = productRepository.load(command.getProductId());
98 |
99 | Sprint sprint = sprintFactory.create(product, command.getSprintName(), command.getSprintBeginDate(),
100 | command.getSprintEndDate());
101 | sprintRepository.save(sprint);
102 |
103 | return sprint.getId();
104 | }
105 |
106 | }
107 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/application/impl/ReleaseServiceImpl.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.application.impl;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 |
5 | import example.ddd.ApplicationService;
6 | import example.scrumboard.application.api.ReleaseService;
7 | import example.scrumboard.domain.backlogitem.BacklogItem;
8 | import example.scrumboard.domain.backlogitem.BacklogItemId;
9 | import example.scrumboard.domain.backlogitem.BacklogItemRepository;
10 | import example.scrumboard.domain.release.Release;
11 | import example.scrumboard.domain.release.ReleaseId;
12 | import example.scrumboard.domain.release.ReleaseRepository;
13 |
14 | @ApplicationService
15 | public class ReleaseServiceImpl implements ReleaseService {
16 |
17 | @Autowired
18 | private ReleaseRepository releaseRepository;
19 |
20 | @Autowired
21 | private BacklogItemRepository backlogItemRepository;
22 |
23 | @Override
24 | public void scheduleBacklogItem(ReleaseId releaseId, BacklogItemId backlogItemId) {
25 | Release release = releaseRepository.load(releaseId);
26 |
27 | BacklogItem backlogItem = backlogItemRepository.load(backlogItemId);
28 | backlogItem.scheduleToRelease(release);
29 |
30 | release.scheduleBacklogItem(backlogItem);
31 |
32 | releaseRepository.save(release);
33 | }
34 |
35 | @Override
36 | public void unscheduleBacklogItem(ReleaseId releaseId, BacklogItemId backlogItemId) {
37 | Release release = releaseRepository.load(releaseId);
38 |
39 | BacklogItem backlogItem = backlogItemRepository.load(backlogItemId);
40 | backlogItem.unscheduleFromRelease(release);
41 |
42 | release.unscheduleBacklogItem(backlogItem);
43 |
44 | releaseRepository.save(release);
45 | }
46 |
47 | @Override
48 | public void archive(ReleaseId releaseId) {
49 | // TODO Auto-generated method stub
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/application/impl/SprintServiceImpl.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.application.impl;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 |
5 | import example.ddd.ApplicationService;
6 | import example.scrumboard.application.api.SprintService;
7 | import example.scrumboard.domain.backlogitem.BacklogItem;
8 | import example.scrumboard.domain.backlogitem.BacklogItemId;
9 | import example.scrumboard.domain.backlogitem.BacklogItemRepository;
10 | import example.scrumboard.domain.sprint.Sprint;
11 | import example.scrumboard.domain.sprint.SprintId;
12 | import example.scrumboard.domain.sprint.SprintRepository;
13 |
14 | @ApplicationService
15 | public class SprintServiceImpl implements SprintService {
16 |
17 | @Autowired
18 | private SprintRepository sprintRepository;
19 |
20 | @Autowired
21 | private BacklogItemRepository backlogItemRepository;
22 |
23 | @Override
24 | public void commitBacklogItem(SprintId sprintId, BacklogItemId backlogItemId) {
25 | Sprint sprint = sprintRepository.load(sprintId);
26 |
27 | BacklogItem backlogItem = backlogItemRepository.load(backlogItemId);
28 | backlogItem.commitToSprint(sprint);
29 |
30 | sprint.commitBacklogItem(backlogItem);
31 |
32 | backlogItemRepository.save(backlogItem);
33 | sprintRepository.save(sprint);
34 | }
35 |
36 | @Override
37 | public void uncommitBacklogItem(SprintId sprintId, BacklogItemId backlogItemId) {
38 | Sprint sprint = sprintRepository.load(sprintId);
39 |
40 | BacklogItem backlogItem = backlogItemRepository.load(backlogItemId);
41 | backlogItem.uncommitFromSprint(sprint);
42 |
43 | sprint.uncommitBacklogItem(backlogItem);
44 |
45 | backlogItemRepository.save(backlogItem);
46 | sprintRepository.save(sprint);
47 | }
48 |
49 | @Override
50 | public void beginSprint(SprintId sprintId) {
51 | // TODO Auto-generated method stub
52 | }
53 |
54 | @Override
55 | public void finishSprint(SprintId sprintId) {
56 | // TODO Auto-generated method stub
57 | }
58 |
59 | @Override
60 | public void captureRetrospective(SprintId sprintId, String retrospective) {
61 | // TODO Auto-generated method stub
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/application/impl/TaskServiceImpl.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.application.impl;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 |
5 | import example.ddd.ApplicationService;
6 | import example.scrumboard.application.api.TaskService;
7 | import example.scrumboard.application.api.commands.CreateTaskCommand;
8 | import example.scrumboard.application.system.DateProvider;
9 | import example.scrumboard.domain.backlogitem.BacklogItem;
10 | import example.scrumboard.domain.backlogitem.BacklogItemRepository;
11 | import example.scrumboard.domain.backlogitem.task.Task;
12 | import example.scrumboard.domain.backlogitem.task.TaskFactory;
13 | import example.scrumboard.domain.backlogitem.task.TaskId;
14 | import example.scrumboard.domain.backlogitem.task.TaskRepository;
15 |
16 | @ApplicationService
17 | public class TaskServiceImpl implements TaskService {
18 |
19 | @Autowired
20 | private TaskFactory taskFactory;
21 |
22 | @Autowired
23 | private TaskRepository taskRepository;
24 |
25 | @Autowired
26 | private BacklogItemRepository backlogItemRepository;
27 |
28 | @Autowired
29 | private DateProvider dateProvider;
30 |
31 | @Override
32 | public TaskId createTask(CreateTaskCommand command) {
33 | BacklogItem backlogItem = backlogItemRepository.load(command.getBacklogItemId());
34 |
35 | Task task = taskFactory.create(backlogItem, command.getTaskName(), command.getTaskDescription(),
36 | command.getHoursRemaining());
37 | taskRepository.save(task);
38 |
39 | return task.getId();
40 | }
41 |
42 | @Override
43 | public void beginTask(TaskId taskId) {
44 | Task task = taskRepository.load(taskId);
45 | task.begin();
46 |
47 | taskRepository.save(task);
48 | }
49 |
50 | @Override
51 | public void finishTask(TaskId taskId) {
52 | Task task = taskRepository.load(taskId);
53 | task.finish();
54 |
55 | taskRepository.save(task);
56 | }
57 |
58 | @Override
59 | public void amendHoursRemaining(TaskId taskId, Integer hoursRemaing) {
60 | Task task = taskRepository.load(taskId);
61 | task.amendHoursRemaining(dateProvider.currentDate(), hoursRemaing);
62 |
63 | taskRepository.save(task);
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/application/system/DateProvider.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.application.system;
2 |
3 | import java.util.Date;
4 |
5 | import org.springframework.stereotype.Component;
6 |
7 | @Component
8 | public class DateProvider {
9 |
10 | public Date currentDate() {
11 | return new Date();
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/application/system/UserProvider.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.application.system;
2 |
3 | import org.springframework.stereotype.Component;
4 |
5 | @Component
6 | public class UserProvider {
7 |
8 | String currentUser() {
9 | return "mickey mouse";
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/application/tasks/SampleTask.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.application.tasks;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 | import org.springframework.scheduling.annotation.Scheduled;
6 | import org.springframework.stereotype.Component;
7 |
8 | @Component
9 | public class SampleTask {
10 |
11 | private static final Logger LOGGER = LoggerFactory.getLogger(SampleTask.class);
12 |
13 | @Scheduled(fixedRate = 60000)
14 | public void log() {
15 | LOGGER.info("Task performed");
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/config/ScrumBoardConfig.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.config;
2 |
3 | import javax.sql.DataSource;
4 |
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.context.annotation.Configuration;
7 | import org.springframework.context.annotation.Import;
8 | import org.springframework.context.annotation.Profile;
9 | import org.springframework.context.annotation.PropertySource;
10 | import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
11 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
12 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
13 |
14 | import example.scrumboard.application.ScrumBoardApplicationConfig;
15 | import example.scrumboard.domain.ScrumBoardDomainConfig;
16 | import example.scrumboard.infrastructure.bootstrap.ScrumBoardInfrastructureBootstrapConfig;
17 | import example.scrumboard.infrastructure.events.ScrumBoardInfrastructureEventsConfig;
18 | import example.scrumboard.infrastructure.jpa.ScrumBoardInfrastructureJpaConfig;
19 | import example.scrumboard.infrastructure.rest.ScrumBoardInfrastructureRestConfig;
20 | import example.scrumboard.infrastructure.shared.ScrumBoardInfrastructureAsyncConfig;
21 | import example.scrumboard.infrastructure.shared.ScrumBoardInfrastructureContextConfig;
22 | import example.scrumboard.infrastructure.shared.ScrumBoardInfrastructureTaskConfig;
23 | import example.scrumboard.infrastructure.shared.ScrumBoardInfrastructureTransactionConfig;
24 | import example.scrumboard.rest.commands.ScrumBoardRestCommandsConfig;
25 | import example.scrumboard.rest.queries.ScrumBoardRestQueriesConfig;
26 |
27 | @Configuration
28 | //@formatter:off
29 | @Import({
30 | // infrastructure shared modules
31 | ScrumBoardInfrastructureAsyncConfig.class,
32 | ScrumBoardInfrastructureContextConfig.class,
33 | ScrumBoardInfrastructureTaskConfig.class,
34 | ScrumBoardInfrastructureTransactionConfig.class,
35 | // infrastructure modules
36 | ScrumBoardInfrastructureBootstrapConfig.class,
37 | ScrumBoardInfrastructureEventsConfig.class,
38 | ScrumBoardInfrastructureJpaConfig.class,
39 | ScrumBoardInfrastructureRestConfig.class,
40 | // core modules
41 | ScrumBoardApplicationConfig.class,
42 | ScrumBoardDomainConfig.class,
43 | ScrumBoardRestCommandsConfig.class,
44 | ScrumBoardRestQueriesConfig.class,
45 | })
46 | //@formatter:on
47 | public class ScrumBoardConfig {
48 |
49 | @Bean
50 | public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
51 | return new PropertySourcesPlaceholderConfigurer();
52 | }
53 |
54 | @Configuration
55 | @PropertySource("classpath:/local.properties")
56 | @Profile(Local.PROFILE)
57 | public static class Local {
58 |
59 | public static final String PROFILE = "local";
60 |
61 | @Bean
62 | public DataSource dataSource() {
63 | return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).build();
64 | }
65 |
66 | }
67 |
68 | @Configuration
69 | @PropertySource("classpath:/remote.properties")
70 | @Profile(Remote.PROFILE)
71 | public static class Remote {
72 |
73 | public static final String PROFILE = "remote";
74 |
75 | @Bean
76 | public DataSource dataSource() {
77 | // TODO: JNDI lookup
78 | return null;
79 | }
80 |
81 | }
82 |
83 | }
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/config/ScrumBoardInitializer.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.config;
2 |
3 | import java.util.EnumSet;
4 |
5 | import javax.servlet.DispatcherType;
6 | import javax.servlet.FilterRegistration;
7 | import javax.servlet.ServletContext;
8 | import javax.servlet.ServletException;
9 | import javax.servlet.ServletRegistration;
10 |
11 | import org.h2.server.web.WebServlet;
12 | import org.springframework.web.WebApplicationInitializer;
13 | import org.springframework.web.context.ContextLoaderListener;
14 | import org.springframework.web.context.WebApplicationContext;
15 | import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
16 | import org.springframework.web.filter.HttpPutFormContentFilter;
17 | import org.springframework.web.servlet.DispatcherServlet;
18 |
19 | public class ScrumBoardInitializer implements WebApplicationInitializer {
20 |
21 | private static final String DISPATCHER_SERVLET_NAME = "rest";
22 | private static final String H2_SERVLET_NAME = "h2";
23 |
24 | @Override
25 | public void onStartup(ServletContext servletContext) throws ServletException {
26 | AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
27 | rootContext.register(ScrumBoardConfig.class);
28 |
29 | servletContext.addListener(new ContextLoaderListener(rootContext));
30 |
31 | registerDispatcherServlet(servletContext, rootContext);
32 | registerH2WebServlet(servletContext);
33 |
34 | registerHttpPutContentFilter(servletContext);
35 | }
36 |
37 | private void registerDispatcherServlet(ServletContext servletContext, WebApplicationContext rootContext) {
38 | ServletRegistration.Dynamic rest = servletContext.addServlet(DISPATCHER_SERVLET_NAME, new DispatcherServlet(
39 | rootContext));
40 | rest.setLoadOnStartup(1);
41 | rest.addMapping("/rest/*");
42 | }
43 |
44 | private void registerH2WebServlet(ServletContext servletContext) {
45 | ServletRegistration.Dynamic h2 = servletContext.addServlet(H2_SERVLET_NAME, new WebServlet());
46 | h2.setLoadOnStartup(2);
47 | h2.addMapping("/h2/*");
48 | }
49 |
50 | private void registerHttpPutContentFilter(ServletContext servletContext) {
51 | FilterRegistration.Dynamic registration = servletContext.addFilter("httpPutFormContentFilter",
52 | HttpPutFormContentFilter.class);
53 | registration.addMappingForServletNames(EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD), false,
54 | DISPATCHER_SERVLET_NAME);
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/ScrumBoardDomainConfig.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain;
2 |
3 | import org.springframework.context.annotation.ComponentScan;
4 | import org.springframework.context.annotation.Configuration;
5 |
6 | @Configuration
7 | @ComponentScan
8 | public class ScrumBoardDomainConfig {
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/backlogitem/BacklogItem.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.backlogitem;
2 |
3 | import static java.util.Objects.requireNonNull;
4 |
5 | import javax.persistence.AttributeOverride;
6 | import javax.persistence.Column;
7 | import javax.persistence.Embedded;
8 | import javax.persistence.Entity;
9 |
10 | import example.ddd.AggregateRoot;
11 | import example.scrumboard.domain.product.Product;
12 | import example.scrumboard.domain.product.ProductId;
13 | import example.scrumboard.domain.release.Release;
14 | import example.scrumboard.domain.sprint.Sprint;
15 | import example.scrumboard.domain.sprint.SprintId;
16 |
17 | @Entity
18 | public class BacklogItem extends AggregateRoot {
19 |
20 | @Embedded
21 | private ProductId productId;
22 |
23 | @Column(nullable = false)
24 | private String story;
25 |
26 | @Embedded
27 | @AttributeOverride(name = "id", column = @Column(nullable = true))
28 | private SprintId sprintId;
29 |
30 | BacklogItem() {
31 | }
32 |
33 | BacklogItem(BacklogItemId id, Product product, String story) {
34 | super(id);
35 | this.productId = requireNonNull(product).getId();
36 | this.story = requireNonNull(story);
37 | }
38 |
39 | BacklogItem(BacklogItemId id, Product product, String story, Sprint sprint) {
40 | this(id, product, story);
41 | if (sprint != null) {
42 | this.sprintId = sprint.getId();
43 | }
44 | }
45 |
46 | public void commitToSprint(Sprint sprint) {
47 | requireNonNull(sprint);
48 | checkProduct(sprint.getProductId());
49 |
50 | if (isCommited()) {
51 | if (isEqualTo(sprint)) {
52 | // do nothing
53 | return;
54 | } else {
55 | uncommitFromSprint();
56 | }
57 | }
58 |
59 | this.sprintId = sprint.getId();
60 | register(new BacklogItemCommited(getId(), sprint.getId()));
61 | }
62 |
63 | public void uncommitFromSprint(Sprint sprint) {
64 | requireNonNull(sprint);
65 | checkProduct(sprint.getProductId());
66 |
67 | if (!isCommited()) {
68 | throw new IllegalArgumentException("Backlog item " + getId() + " is not commited.");
69 | }
70 |
71 | if (!isEqualTo(sprint)) {
72 | throw new IllegalArgumentException("Backlog item " + getId() + " is commited to " + sprintId
73 | + " but expected " + sprint.getId() + ".");
74 | }
75 |
76 | uncommitFromSprint();
77 | }
78 |
79 | public void scheduleToRelease(Release release) {
80 | // TODO Auto-generated method stub
81 | }
82 |
83 | public void unscheduleFromRelease(Release release) {
84 | // TODO Auto-generated method stub
85 | }
86 |
87 | public void assignStoryPoints(StoryPoints storyPoints) {
88 | // TODO Auto-generated method stub
89 | }
90 |
91 | public void assignPriority(Priority priority) {
92 | // TODO Auto-generated method stub
93 | }
94 |
95 | public void checkProduct(ProductId productId) {
96 | if (!this.productId.equals(productId)) {
97 | throw new IllegalArgumentException("Products do not match, product was " + productId + " but expected "
98 | + this.productId + ".");
99 | }
100 | }
101 |
102 | ProductId getProductId() {
103 | return productId;
104 | }
105 |
106 | SprintId getSprintId() {
107 | return sprintId;
108 | }
109 |
110 | private boolean isCommited() {
111 | return sprintId != null;
112 | }
113 |
114 | private boolean isEqualTo(Sprint sprint) {
115 | return sprintId.equals(sprint.getId());
116 | }
117 |
118 | private void uncommitFromSprint() {
119 | SprintId sprintId = this.sprintId;
120 | this.sprintId = null;
121 | register(new BacklogItemUncommited(getId(), sprintId));
122 | }
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/backlogitem/BacklogItemCommited.groovy:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.backlogitem
2 |
3 | import static java.util.Objects.requireNonNull
4 | import example.ddd.Event
5 | import example.scrumboard.domain.sprint.SprintId
6 | import groovy.transform.Immutable
7 | import groovy.transform.TypeChecked
8 |
9 | @Immutable(knownImmutableClasses = [BacklogItemId.class, SprintId.class])
10 | @TypeChecked
11 | class BacklogItemCommited implements Event {
12 | BacklogItemId backlogItemId
13 | SprintId sprintId
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/backlogitem/BacklogItemFactory.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.backlogitem;
2 |
3 | import java.util.UUID;
4 |
5 | import org.springframework.stereotype.Component;
6 |
7 | import example.scrumboard.domain.product.Product;
8 |
9 | @Component
10 | public class BacklogItemFactory {
11 |
12 | public BacklogItem create(Product product, String story) {
13 | BacklogItemId id = new BacklogItemId(UUID.randomUUID().toString());
14 | return new BacklogItem(id, product, story);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/backlogitem/BacklogItemId.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.backlogitem;
2 |
3 | import static java.util.Objects.requireNonNull;
4 |
5 | import javax.persistence.Column;
6 | import javax.persistence.Embeddable;
7 |
8 | import example.ddd.ValueObject;
9 |
10 | @Embeddable
11 | public class BacklogItemId extends ValueObject {
12 |
13 | private static final long serialVersionUID = 1L;
14 |
15 | @Column(name = "backlog_item_id", nullable = false)
16 | private String id;
17 |
18 | BacklogItemId() {
19 | }
20 |
21 | public BacklogItemId(String id) {
22 | this.id = requireNonNull(id);
23 | }
24 |
25 | public String getId() {
26 | return id;
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/backlogitem/BacklogItemRepository.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.backlogitem;
2 |
3 | import example.ddd.Repository;
4 |
5 | public interface BacklogItemRepository extends Repository {
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/backlogitem/BacklogItemUncommited.groovy:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.backlogitem;
2 |
3 | import static java.util.Objects.requireNonNull
4 | import example.ddd.Event
5 | import example.scrumboard.domain.sprint.SprintId
6 | import groovy.transform.Immutable
7 | import groovy.transform.TypeChecked
8 |
9 | @Immutable(knownImmutableClasses = [BacklogItemId.class, SprintId.class])
10 | @TypeChecked
11 | class BacklogItemUncommited implements Event {
12 | BacklogItemId backlogItemId;
13 | SprintId sprintId;
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/backlogitem/Priority.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.backlogitem;
2 |
3 | public class Priority {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/backlogitem/StoryPoints.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.backlogitem;
2 |
3 | public enum StoryPoints {
4 |
5 | ZERO(0), ONE(1), TWO(2), THREE(3), FIVE(5), EIGHT(8), THIRTEEN(13);
6 |
7 | private int value;
8 |
9 | private StoryPoints(int value) {
10 | this.value = value;
11 | }
12 |
13 | public int getValue() {
14 | return value;
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/backlogitem/task/DoneTaskState.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.backlogitem.task;
2 |
3 | public class DoneTaskState extends TaskStateAdapter {
4 |
5 | @Override
6 | public boolean isDone() {
7 | return true;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/backlogitem/task/IllegalTaskStateException.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.backlogitem.task;
2 |
3 | public class IllegalTaskStateException extends RuntimeException {
4 |
5 | private static final long serialVersionUID = 1L;
6 |
7 | private Task task;
8 |
9 | private String operation;
10 |
11 | public IllegalTaskStateException(Task task, String operation) {
12 | this.task = task;
13 | this.operation = operation;
14 | }
15 |
16 | @Override
17 | public String getMessage() {
18 | return "Cannot " + operation + " on " + task + ".";
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/backlogitem/task/InProgressTaskState.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.backlogitem.task;
2 |
3 | import java.util.Date;
4 |
5 | public class InProgressTaskState extends TaskStateAdapter {
6 |
7 | @Override
8 | public boolean isInProgress() {
9 | return true;
10 | }
11 |
12 | @Override
13 | public void finish(Task task) {
14 | task.doFinish();
15 | }
16 |
17 | @Override
18 | public void amendHoursRemaining(Task task, Date effectiveDate, Integer hoursRemaing) {
19 | task.doAmendHoursRemaining(effectiveDate, hoursRemaing);
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/backlogitem/task/RemainingAmendment.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.backlogitem.task;
2 |
3 | import static java.util.Objects.requireNonNull;
4 |
5 | import java.util.Date;
6 |
7 | import javax.persistence.Column;
8 | import javax.persistence.Entity;
9 | import javax.persistence.GeneratedValue;
10 | import javax.persistence.Id;
11 |
12 | @Entity
13 | public class RemainingAmendment {
14 |
15 | @Id
16 | @GeneratedValue
17 | private Long entityId;
18 |
19 | @Column(nullable = false)
20 | private Date effectiveDate;
21 |
22 | @Column(nullable = false)
23 | private Integer hoursRemaining;
24 |
25 | RemainingAmendment() {
26 | }
27 |
28 | RemainingAmendment(Date effectiveDate, Integer hoursRemaining) {
29 | this.effectiveDate = requireNonNull(effectiveDate);
30 | this.hoursRemaining = requireNonNull(hoursRemaining);
31 |
32 | if (hoursRemaining < 0) {
33 | throw new IllegalArgumentException("Remaining hours must be positive but was " + hoursRemaining);
34 | }
35 | }
36 |
37 | Date getEffectiveDate() {
38 | return effectiveDate;
39 | }
40 |
41 | Integer getHoursRemaining() {
42 | return hoursRemaining;
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/backlogitem/task/Task.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.backlogitem.task;
2 |
3 | import static java.util.Objects.requireNonNull;
4 |
5 | import java.util.Date;
6 | import java.util.List;
7 |
8 | import javax.persistence.CascadeType;
9 | import javax.persistence.Column;
10 | import javax.persistence.Embedded;
11 | import javax.persistence.Entity;
12 | import javax.persistence.EnumType;
13 | import javax.persistence.Enumerated;
14 | import javax.persistence.FetchType;
15 | import javax.persistence.JoinColumn;
16 | import javax.persistence.OneToMany;
17 | import javax.persistence.OrderColumn;
18 |
19 | import example.ddd.AggregateRoot;
20 | import example.scrumboard.domain.backlogitem.BacklogItem;
21 | import example.scrumboard.domain.backlogitem.BacklogItemId;
22 |
23 | @Entity
24 | public class Task extends AggregateRoot {
25 |
26 | @Embedded
27 | private BacklogItemId backlogItemId;
28 |
29 | @Enumerated(EnumType.STRING)
30 | private TaskStatus status;
31 |
32 | @Column(nullable = false)
33 | private String name;
34 |
35 | @Column(nullable = false)
36 | private String description;
37 |
38 | @Column(nullable = false)
39 | private Integer hoursRemaining;
40 |
41 | @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
42 | @JoinColumn(name = "task_id", nullable = false, insertable = false, updatable = false)
43 | @OrderColumn
44 | private List remainingAmendments;
45 |
46 | Task() {
47 | }
48 |
49 | Task(TaskId id, BacklogItem backlogItem, TaskStatus status, String name, String description,
50 | Integer hoursRemaining, List remainingAmendments) {
51 | super(id);
52 | this.backlogItemId = requireNonNull(backlogItem).getId();
53 | this.status = requireNonNull(status);
54 | this.name = requireNonNull(name);
55 | this.description = requireNonNull(description);
56 | this.hoursRemaining = requireNonNull(hoursRemaining);
57 | this.remainingAmendments = requireNonNull(remainingAmendments);
58 | }
59 |
60 | public void begin() {
61 | status.begin(this);
62 | }
63 |
64 | void doBegin() {
65 | status = TaskStatus.IN_PROGRESS;
66 | }
67 |
68 | public void finish() {
69 | status.finish(this);
70 | }
71 |
72 | void doFinish() {
73 | status = TaskStatus.DONE;
74 | }
75 |
76 | public void amendHoursRemaining(Date effectiveDate, Integer hoursRemaing) {
77 | status.amendHoursRemaining(this, effectiveDate, hoursRemaing);
78 | }
79 |
80 | void doAmendHoursRemaining(Date effectiveDate, Integer hoursRemaing) {
81 | RemainingAmendment remainingAmendment = new RemainingAmendment(effectiveDate, hoursRemaing);
82 | remainingAmendments.add(remainingAmendment);
83 | }
84 |
85 | BacklogItemId getBacklogItemId() {
86 | return backlogItemId;
87 | }
88 |
89 | TaskStatus getStatus() {
90 | return status;
91 | }
92 |
93 | String getName() {
94 | return name;
95 | }
96 |
97 | String getDescription() {
98 | return description;
99 | }
100 |
101 | Integer getHoursRemaining() {
102 | return hoursRemaining;
103 | }
104 |
105 | List getRemainingAmendments() {
106 | return remainingAmendments;
107 | }
108 |
109 | }
110 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/backlogitem/task/TaskFactory.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.backlogitem.task;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 | import java.util.UUID;
6 |
7 | import org.springframework.stereotype.Component;
8 |
9 | import example.scrumboard.domain.backlogitem.BacklogItem;
10 |
11 | @Component
12 | public class TaskFactory {
13 |
14 | public Task create(BacklogItem backlogItem, String name, String description, Integer hoursRemaining) {
15 | TaskId id = new TaskId(UUID.randomUUID().toString());
16 | TaskStatus status = TaskStatus.TODO;
17 | List remainingAmendments = new ArrayList<>();
18 |
19 | return new Task(id, backlogItem, status, name, description, hoursRemaining, remainingAmendments);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/backlogitem/task/TaskId.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.backlogitem.task;
2 |
3 | import static java.util.Objects.requireNonNull;
4 |
5 | import javax.persistence.Column;
6 | import javax.persistence.Embeddable;
7 |
8 | import example.ddd.ValueObject;
9 |
10 | @Embeddable
11 | public class TaskId extends ValueObject {
12 |
13 | private static final long serialVersionUID = 1L;
14 |
15 | @Column(name = "task_id", nullable = false)
16 | private String id;
17 |
18 | TaskId() {
19 | }
20 |
21 | public TaskId(String id) {
22 | this.id = requireNonNull(id);
23 | }
24 |
25 | public String getId() {
26 | return id;
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/backlogitem/task/TaskRepository.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.backlogitem.task;
2 |
3 | import example.ddd.Repository;
4 |
5 | public interface TaskRepository extends Repository {
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/backlogitem/task/TaskState.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.backlogitem.task;
2 |
3 | import java.util.Date;
4 |
5 | public interface TaskState {
6 |
7 | boolean isTodo();
8 |
9 | boolean isInProgress();
10 |
11 | boolean isDone();
12 |
13 | void begin(Task task);
14 |
15 | void finish(Task task);
16 |
17 | void amendHoursRemaining(Task task, Date effectiveDate, Integer hoursRemaing);
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/backlogitem/task/TaskStateAdapter.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.backlogitem.task;
2 |
3 | import java.util.Date;
4 |
5 | public class TaskStateAdapter implements TaskState {
6 |
7 | @Override
8 | public boolean isTodo() {
9 | return false;
10 | }
11 |
12 | @Override
13 | public boolean isInProgress() {
14 | return false;
15 | }
16 |
17 | @Override
18 | public boolean isDone() {
19 | return false;
20 | }
21 |
22 | @Override
23 | public void begin(Task task) {
24 | throw new IllegalTaskStateException(task, "start");
25 | }
26 |
27 | @Override
28 | public void finish(Task task) {
29 | throw new IllegalTaskStateException(task, "finish");
30 | }
31 |
32 | @Override
33 | public void amendHoursRemaining(Task task, Date effectiveDate, Integer hoursRemaing) {
34 | throw new IllegalTaskStateException(task, "amend hours remaining");
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/backlogitem/task/TaskStatus.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.backlogitem.task;
2 |
3 | import java.util.Date;
4 |
5 | public enum TaskStatus implements TaskState {
6 |
7 | // @formatter:off
8 | TODO(new TodoTaskState()),
9 | IN_PROGRESS(new InProgressTaskState()),
10 | DONE(new DoneTaskState());
11 | // @formatter:on
12 |
13 | private TaskState state;
14 |
15 | private TaskStatus(TaskState state) {
16 | this.state = state;
17 | }
18 |
19 | @Override
20 | public boolean isTodo() {
21 | return state.isTodo();
22 | }
23 |
24 | @Override
25 | public boolean isInProgress() {
26 | return state.isInProgress();
27 | }
28 |
29 | @Override
30 | public boolean isDone() {
31 | return state.isDone();
32 | }
33 |
34 | @Override
35 | public void begin(Task task) {
36 | state.begin(task);
37 | }
38 |
39 | @Override
40 | public void finish(Task task) {
41 | state.finish(task);
42 | }
43 |
44 | @Override
45 | public void amendHoursRemaining(Task task, Date effectiveDate, Integer hoursRemaing) {
46 | state.amendHoursRemaining(task, effectiveDate, hoursRemaing);
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/backlogitem/task/TodoTaskState.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.backlogitem.task;
2 |
3 | public class TodoTaskState extends TaskStateAdapter {
4 |
5 | @Override
6 | public boolean isTodo() {
7 | return true;
8 | }
9 |
10 | @Override
11 | public void begin(Task task) {
12 | task.doBegin();
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/product/BacklogItemPlannedEvent.groovy:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.product
2 |
3 | import static java.util.Objects.requireNonNull
4 | import example.ddd.Event
5 | import example.scrumboard.domain.backlogitem.BacklogItemId
6 | import groovy.transform.Immutable
7 | import groovy.transform.TypeChecked
8 |
9 | @Immutable(knownImmutableClasses = [ProductId.class, BacklogItemId.class])
10 | @TypeChecked
11 | class BacklogItemPlannedEvent implements Event {
12 | ProductId productId
13 | BacklogItemId backlogItemId
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/product/Product.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.product;
2 |
3 | import static com.google.common.collect.Lists.newArrayList;
4 | import static java.util.Objects.requireNonNull;
5 |
6 | import java.util.List;
7 | import java.util.Set;
8 |
9 | import javax.persistence.CascadeType;
10 | import javax.persistence.Column;
11 | import javax.persistence.Entity;
12 | import javax.persistence.FetchType;
13 | import javax.persistence.JoinColumn;
14 | import javax.persistence.OneToMany;
15 |
16 | import com.google.common.collect.Iterables;
17 |
18 | import example.ddd.AggregateRoot;
19 | import example.scrumboard.domain.backlogitem.BacklogItem;
20 | import example.scrumboard.domain.backlogitem.BacklogItemId;
21 |
22 | @Entity
23 | public class Product extends AggregateRoot {
24 |
25 | @Column(nullable = false, unique = true)
26 | private String name;
27 |
28 | @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
29 | @JoinColumn(name = "product_id", nullable = false, insertable = false, updatable = false)
30 | private Set backlogItems;
31 |
32 | Product() {
33 | }
34 |
35 | Product(ProductId id, String name, Set backlogItems) {
36 | super(id);
37 | this.name = requireNonNull(name);
38 | this.backlogItems = requireNonNull(backlogItems);
39 | }
40 |
41 | public void planBacklogItem(BacklogItem backlogItem) {
42 | requireNonNull(backlogItem);
43 | backlogItem.checkProduct(getId());
44 |
45 | BacklogItemId backlogItemId = backlogItem.getId();
46 | if (Iterables.any(backlogItems, ProductBacklogItem.hasId(backlogItemId))) {
47 | throw new IllegalArgumentException("Backlog item is already planned " + backlogItemId);
48 | }
49 |
50 | int position = backlogItems.size();
51 | backlogItems.add(new ProductBacklogItem(backlogItemId, position));
52 |
53 | register(new BacklogItemPlannedEvent(getId(), backlogItemId));
54 | }
55 |
56 | public void reorderBacklogItems(List backlogItemIds) {
57 | requireNonNull(backlogItemIds);
58 |
59 | for (ProductBacklogItem backlogItem : backlogItems) {
60 | BacklogItemId backlogItemId = backlogItem.getId();
61 |
62 | int newPosition = backlogItemIds.indexOf(backlogItemId);
63 | if (newPosition == -1) {
64 | throw new IllegalArgumentException("Backlog item not found " + backlogItemId);
65 | }
66 |
67 | backlogItem.setPosition(newPosition);
68 | }
69 |
70 | }
71 |
72 | public void reorderBacklogItems(BacklogItemId... backlogItemIds) {
73 | requireNonNull(backlogItemIds);
74 | reorderBacklogItems(newArrayList(backlogItemIds));
75 | }
76 |
77 | String getName() {
78 | return name;
79 | }
80 |
81 | Set getBacklogItems() {
82 | return backlogItems;
83 | }
84 |
85 | }
86 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/product/ProductBacklogItem.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.product;
2 |
3 | import static java.util.Objects.requireNonNull;
4 |
5 | import javax.persistence.Column;
6 | import javax.persistence.Embedded;
7 | import javax.persistence.Entity;
8 | import javax.persistence.GeneratedValue;
9 | import javax.persistence.Id;
10 |
11 | import com.google.common.base.Predicate;
12 |
13 | import example.scrumboard.domain.backlogitem.BacklogItemId;
14 |
15 | @Entity
16 | public class ProductBacklogItem {
17 |
18 | static Predicate hasId(final BacklogItemId id) {
19 | return new Predicate() {
20 | @Override
21 | public boolean apply(ProductBacklogItem input) {
22 | return input.getId().equals(id);
23 | }
24 | };
25 | }
26 |
27 | @Id
28 | @GeneratedValue
29 | private Long entityId;
30 |
31 | @Embedded
32 | private BacklogItemId id;
33 |
34 | @Column(nullable = false)
35 | private Integer position;
36 |
37 | ProductBacklogItem() {
38 | }
39 |
40 | ProductBacklogItem(BacklogItemId id, Integer position) {
41 | this.id = requireNonNull(id);
42 | this.position = requireNonNull(position);
43 | }
44 |
45 | BacklogItemId getId() {
46 | return id;
47 | }
48 |
49 | Integer getPosition() {
50 | return position;
51 | }
52 |
53 | void setPosition(Integer position) {
54 | this.position = position;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/product/ProductFactory.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.product;
2 |
3 | import java.util.HashSet;
4 | import java.util.Set;
5 | import java.util.UUID;
6 |
7 | import org.springframework.stereotype.Component;
8 |
9 | @Component
10 | public class ProductFactory {
11 |
12 | public Product create(String name) {
13 | ProductId id = new ProductId(UUID.randomUUID().toString());
14 | Set backlogItems = new HashSet<>();
15 |
16 | return new Product(id, name, backlogItems);
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/product/ProductId.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.product;
2 |
3 | import static java.util.Objects.requireNonNull;
4 |
5 | import javax.persistence.Column;
6 | import javax.persistence.Embeddable;
7 |
8 | import example.ddd.ValueObject;
9 |
10 | @Embeddable
11 | public class ProductId extends ValueObject {
12 |
13 | private static final long serialVersionUID = 1L;
14 |
15 | @Column(name = "product_id", nullable = false)
16 | private String id;
17 |
18 | ProductId() {
19 | }
20 |
21 | public ProductId(String id) {
22 | this.id = requireNonNull(id);
23 | }
24 |
25 | public String getId() {
26 | return id;
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/product/ProductRepository.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.product;
2 |
3 | import example.ddd.Repository;
4 |
5 | public interface ProductRepository extends Repository {
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/release/Release.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.release;
2 |
3 | import static java.util.Objects.requireNonNull;
4 |
5 | import java.util.Date;
6 | import java.util.Set;
7 |
8 | import javax.persistence.CascadeType;
9 | import javax.persistence.Column;
10 | import javax.persistence.Embedded;
11 | import javax.persistence.Entity;
12 | import javax.persistence.FetchType;
13 | import javax.persistence.JoinColumn;
14 | import javax.persistence.OneToMany;
15 |
16 | import example.ddd.AggregateRoot;
17 | import example.scrumboard.domain.backlogitem.BacklogItem;
18 | import example.scrumboard.domain.product.Product;
19 | import example.scrumboard.domain.product.ProductId;
20 |
21 | @Entity
22 | public class Release extends AggregateRoot {
23 |
24 | @Embedded
25 | private ProductId productId;
26 |
27 | @Column(nullable = false)
28 | private String name;
29 |
30 | @Column(nullable = false)
31 | private Date releaseDate;
32 |
33 | @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
34 | @JoinColumn(name = "release_id", nullable = false, insertable = false, updatable = false)
35 | private Set backlogItems;
36 |
37 | Release() {
38 | }
39 |
40 | Release(ReleaseId id, Product product, String name, Date releaseDate, Set backlogItems) {
41 | super(id);
42 | this.productId = requireNonNull(product).getId();
43 | this.name = requireNonNull(name);
44 | this.releaseDate = requireNonNull(releaseDate);
45 | this.backlogItems = requireNonNull(backlogItems);
46 | }
47 |
48 | public ProductId getProductId() {
49 | return productId;
50 | }
51 |
52 | public void scheduleBacklogItem(BacklogItem backlogItem) {
53 | // TODO Auto-generated method stub
54 | }
55 |
56 | public void unscheduleBacklogItem(BacklogItem backlogItem) {
57 | // TODO Auto-generated method stub
58 | }
59 |
60 | String getName() {
61 | return name;
62 | }
63 |
64 | Date getReleaseDate() {
65 | return releaseDate;
66 | }
67 |
68 | Set getBacklogItems() {
69 | return backlogItems;
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/release/ReleaseFactory.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.release;
2 |
3 | import java.util.Date;
4 | import java.util.HashSet;
5 | import java.util.Set;
6 | import java.util.UUID;
7 |
8 | import org.springframework.stereotype.Component;
9 |
10 | import example.scrumboard.domain.product.Product;
11 |
12 | @Component
13 | public class ReleaseFactory {
14 |
15 | public Release create(Product product, String name, Date releaseDate) {
16 | ReleaseId id = new ReleaseId(UUID.randomUUID().toString());
17 | Set backlogItems = new HashSet<>();
18 |
19 | return new Release(id, product, name, releaseDate, backlogItems);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/release/ReleaseId.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.release;
2 |
3 | import static java.util.Objects.requireNonNull;
4 |
5 | import javax.persistence.Column;
6 | import javax.persistence.Embeddable;
7 |
8 | import example.ddd.ValueObject;
9 |
10 | @Embeddable
11 | public class ReleaseId extends ValueObject {
12 |
13 | private static final long serialVersionUID = 1L;
14 |
15 | @Column(name = "release_id", nullable = false)
16 | private String id;
17 |
18 | ReleaseId() {
19 | }
20 |
21 | public ReleaseId(String id) {
22 | this.id = requireNonNull(id);
23 | }
24 |
25 | public String getId() {
26 | return id;
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/release/ReleaseRepository.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.release;
2 |
3 | import example.ddd.Repository;
4 |
5 | public interface ReleaseRepository extends Repository {
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/release/ScheduledBacklogItem.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.release;
2 |
3 | import static java.util.Objects.requireNonNull;
4 |
5 | import javax.persistence.Embedded;
6 | import javax.persistence.Entity;
7 | import javax.persistence.GeneratedValue;
8 | import javax.persistence.Id;
9 |
10 | import com.google.common.base.Predicate;
11 |
12 | import example.scrumboard.domain.backlogitem.BacklogItemId;
13 |
14 | @Entity
15 | public class ScheduledBacklogItem {
16 |
17 | static Predicate hasId(final BacklogItemId id) {
18 | return new Predicate() {
19 | @Override
20 | public boolean apply(ScheduledBacklogItem input) {
21 | return input.getId().equals(id);
22 | }
23 | };
24 | }
25 |
26 | @Id
27 | @GeneratedValue
28 | private Long entityId;
29 |
30 | @Embedded
31 | private BacklogItemId id;
32 |
33 | ScheduledBacklogItem() {
34 | }
35 |
36 | ScheduledBacklogItem(BacklogItemId id) {
37 | this.id = requireNonNull(id);
38 | }
39 |
40 | BacklogItemId getId() {
41 | return id;
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/sprint/CommitedBacklogItem.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.sprint;
2 |
3 | import static java.util.Objects.requireNonNull;
4 |
5 | import javax.persistence.Embedded;
6 | import javax.persistence.Entity;
7 | import javax.persistence.GeneratedValue;
8 | import javax.persistence.Id;
9 |
10 | import com.google.common.base.Predicate;
11 |
12 | import example.scrumboard.domain.backlogitem.BacklogItemId;
13 |
14 | @Entity
15 | public class CommitedBacklogItem {
16 |
17 | static Predicate hasId(final BacklogItemId id) {
18 | return new Predicate() {
19 | @Override
20 | public boolean apply(CommitedBacklogItem input) {
21 | return input.getId().equals(id);
22 | }
23 | };
24 | }
25 |
26 | @Id
27 | @GeneratedValue
28 | private Long entityId;
29 |
30 | @Embedded
31 | private BacklogItemId id;
32 |
33 | CommitedBacklogItem() {
34 | }
35 |
36 | CommitedBacklogItem(BacklogItemId id) {
37 | this.id = requireNonNull(id);
38 | }
39 |
40 | BacklogItemId getId() {
41 | return id;
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/sprint/Sprint.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.sprint;
2 |
3 | import static java.util.Objects.requireNonNull;
4 |
5 | import java.util.Date;
6 | import java.util.Set;
7 |
8 | import javax.persistence.CascadeType;
9 | import javax.persistence.Column;
10 | import javax.persistence.Embedded;
11 | import javax.persistence.Entity;
12 | import javax.persistence.FetchType;
13 | import javax.persistence.JoinColumn;
14 | import javax.persistence.OneToMany;
15 |
16 | import com.google.common.collect.Iterables;
17 |
18 | import example.ddd.AggregateRoot;
19 | import example.scrumboard.domain.backlogitem.BacklogItem;
20 | import example.scrumboard.domain.backlogitem.BacklogItemId;
21 | import example.scrumboard.domain.product.Product;
22 | import example.scrumboard.domain.product.ProductId;
23 |
24 | @Entity
25 | public class Sprint extends AggregateRoot {
26 |
27 | @Embedded
28 | private ProductId productId;
29 |
30 | @Column(nullable = false)
31 | private String name;
32 |
33 | @Column(nullable = false)
34 | private Date beginDate;
35 |
36 | @Column(nullable = false)
37 | private Date endDate;
38 |
39 | @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
40 | @JoinColumn(name = "sprint_id", nullable = false, insertable = false, updatable = false)
41 | private Set backlogItems;
42 |
43 | Sprint(SprintId id, Product product, String name, Date beginDate, Date endDate,
44 | Set backlogItems) {
45 | super(id);
46 | this.productId = requireNonNull(product).getId();
47 | this.name = requireNonNull(name);
48 | this.beginDate = requireNonNull(beginDate);
49 | this.endDate = requireNonNull(endDate);
50 | this.backlogItems = requireNonNull(backlogItems);
51 |
52 | if (beginDate.after(endDate)) {
53 | throw new IllegalArgumentException("Begin date must be before end date " + endDate + " but was "
54 | + beginDate + ".");
55 | }
56 | }
57 |
58 | public ProductId getProductId() {
59 | return productId;
60 | }
61 |
62 | public void commitBacklogItem(BacklogItem backlogItem) {
63 | requireNonNull(backlogItem);
64 | backlogItem.checkProduct(productId);
65 |
66 | BacklogItemId backlogItemId = backlogItem.getId();
67 | if (Iterables.any(backlogItems, CommitedBacklogItem.hasId(backlogItemId))) {
68 | throw new IllegalArgumentException("Backlog item is already commited " + backlogItemId);
69 | }
70 |
71 | backlogItems.add(new CommitedBacklogItem(backlogItemId));
72 | }
73 |
74 | public void uncommitBacklogItem(BacklogItem backlogItem) {
75 | requireNonNull(backlogItem);
76 | backlogItem.checkProduct(productId);
77 |
78 | BacklogItemId backlogItemId = backlogItem.getId();
79 | boolean removed = Iterables.removeIf(backlogItems, CommitedBacklogItem.hasId(backlogItemId));
80 | if (!removed) {
81 | throw new IllegalArgumentException("Backlog item is not commited " + backlogItemId);
82 | }
83 | }
84 |
85 | String getName() {
86 | return name;
87 | }
88 |
89 | Date getBeginDate() {
90 | return beginDate;
91 | }
92 |
93 | Date getEndDate() {
94 | return endDate;
95 | }
96 |
97 | Set getBacklogItems() {
98 | return backlogItems;
99 | }
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/sprint/SprintFactory.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.sprint;
2 |
3 | import java.util.Date;
4 | import java.util.HashSet;
5 | import java.util.Set;
6 | import java.util.UUID;
7 |
8 | import org.springframework.stereotype.Component;
9 |
10 | import example.scrumboard.domain.product.Product;
11 |
12 | @Component
13 | public class SprintFactory {
14 |
15 | public Sprint create(Product product, String name, Date beginDate, Date endDate) {
16 | SprintId id = new SprintId(UUID.randomUUID().toString());
17 | Set backlogItems = new HashSet<>();
18 |
19 | return new Sprint(id, product, name, beginDate, endDate, backlogItems);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/sprint/SprintId.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.sprint;
2 |
3 | import static java.util.Objects.requireNonNull;
4 |
5 | import javax.persistence.Column;
6 | import javax.persistence.Embeddable;
7 |
8 | import example.ddd.ValueObject;
9 |
10 | @Embeddable
11 | public class SprintId extends ValueObject {
12 |
13 | private static final long serialVersionUID = 1L;
14 |
15 | @Column(name = "sprint_id", nullable = false)
16 | private String id;
17 |
18 | SprintId() {
19 | }
20 |
21 | public SprintId(String id) {
22 | this.id = requireNonNull(id);
23 | }
24 |
25 | public String getId() {
26 | return id;
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/domain/sprint/SprintRepository.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.sprint;
2 |
3 | import example.ddd.Repository;
4 |
5 | public interface SprintRepository extends Repository {
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/infrastructure/bootstrap/BootstrapEventPublisher.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.infrastructure.bootstrap;
2 |
3 | import org.springframework.context.ApplicationContext;
4 | import org.springframework.context.ApplicationListener;
5 | import org.springframework.context.event.ContextRefreshedEvent;
6 | import org.springframework.scheduling.annotation.Async;
7 | import org.springframework.stereotype.Component;
8 |
9 | import example.bootstrap.BootstrapEvent;
10 |
11 | @Component
12 | public class BootstrapEventPublisher implements ApplicationListener {
13 |
14 | @Override
15 | @Async
16 | public void onApplicationEvent(ContextRefreshedEvent event) {
17 | ApplicationContext applicationContext = event.getApplicationContext();
18 |
19 | if (isRootApplicationContext(applicationContext)) {
20 | applicationContext.publishEvent(new BootstrapEvent(applicationContext));
21 | }
22 | }
23 |
24 | private boolean isRootApplicationContext(ApplicationContext applicationContext) {
25 | return applicationContext.getParent() == null;
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/infrastructure/bootstrap/ScrumBoardInfrastructureBootstrapConfig.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.infrastructure.bootstrap;
2 |
3 | import org.springframework.context.annotation.ComponentScan;
4 | import org.springframework.context.annotation.Configuration;
5 |
6 | @Configuration
7 | @ComponentScan
8 | public class ScrumBoardInfrastructureBootstrapConfig {
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/infrastructure/events/ScrumBoardInfrastructureEventsConfig.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.infrastructure.events;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.ComponentScan;
5 | import org.springframework.context.annotation.Configuration;
6 | import org.springframework.integration.channel.PublishSubscribeChannel;
7 | import org.springframework.integration.config.EnableIntegration;
8 | import org.springframework.integration.core.MessagingTemplate;
9 |
10 | @Configuration
11 | @EnableIntegration
12 | @ComponentScan
13 | public class ScrumBoardInfrastructureEventsConfig {
14 |
15 | @Bean
16 | public PublishSubscribeChannel eventBus() {
17 | return new PublishSubscribeChannel();
18 | }
19 |
20 | @Bean
21 | public MessagingTemplate messagingTemplate(PublishSubscribeChannel eventBus) {
22 | MessagingTemplate messagingTemplate = new MessagingTemplate();
23 | messagingTemplate.setDefaultChannel(eventBus);
24 |
25 | return messagingTemplate;
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/infrastructure/events/SpringIntegrationEventPublisher.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.infrastructure.events;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.integration.core.MessagingTemplate;
5 | import org.springframework.integration.support.MessageBuilder;
6 | import org.springframework.messaging.Message;
7 | import org.springframework.stereotype.Component;
8 |
9 | import example.ddd.Event;
10 | import example.ddd.EventPublisher;
11 |
12 | @Component
13 | public class SpringIntegrationEventPublisher implements EventPublisher {
14 |
15 | @Autowired
16 | private MessagingTemplate messagingTemplate;
17 |
18 | @Override
19 | public void publish(Event event) {
20 | Message message = MessageBuilder.withPayload(event).build();
21 | messagingTemplate.send(message);
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/infrastructure/jpa/ScrumBoardInfrastructureJpaConfig.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.infrastructure.jpa;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | import javax.sql.DataSource;
7 |
8 | import org.hibernate.jpa.AvailableSettings;
9 | import org.springframework.beans.factory.annotation.Autowired;
10 | import org.springframework.context.annotation.Bean;
11 | import org.springframework.context.annotation.ComponentScan;
12 | import org.springframework.context.annotation.Configuration;
13 | import org.springframework.core.env.Environment;
14 | import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
15 | import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
16 | import org.springframework.orm.jpa.vendor.Database;
17 | import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
18 |
19 | import example.scrumboard.domain.ScrumBoardDomainConfig;
20 | import example.scrumboard.infrastructure.jpa.hibernate.FixedPrefixNamingStrategy;
21 |
22 | @Configuration
23 | @ComponentScan
24 | public class ScrumBoardInfrastructureJpaConfig {
25 |
26 | @Autowired
27 | private Environment environment;
28 |
29 | @Autowired
30 | private DataSource dataSource;
31 |
32 | @Bean
33 | public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
34 | HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
35 |
36 | jpaVendorAdapter.setDatabase(Database.H2);
37 | jpaVendorAdapter.setGenerateDdl(environment.getRequiredProperty("jpa.generateDdl", Boolean.class));
38 | jpaVendorAdapter.setShowSql(environment.getRequiredProperty("jpa.showSql", Boolean.class));
39 |
40 | Map jpaProperties = new HashMap<>();
41 | jpaProperties.put(AvailableSettings.NAMING_STRATEGY, FixedPrefixNamingStrategy.class.getName());
42 |
43 | LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
44 |
45 | entityManagerFactoryBean.setDataSource(dataSource);
46 | entityManagerFactoryBean.setPackagesToScan(ScrumBoardDomainConfig.class.getPackage().getName());
47 | entityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter);
48 | entityManagerFactoryBean.setJpaPropertyMap(jpaProperties);
49 |
50 | return entityManagerFactoryBean;
51 | }
52 |
53 | @Bean
54 | public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
55 | return new PersistenceExceptionTranslationPostProcessor();
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/infrastructure/jpa/hibernate/FixedPrefixNamingStrategy.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.infrastructure.jpa.hibernate;
2 |
3 | import org.hibernate.cfg.ImprovedNamingStrategy;
4 |
5 | import com.google.common.base.Strings;
6 |
7 | public class FixedPrefixNamingStrategy extends ImprovedNamingStrategy {
8 |
9 | private static final long serialVersionUID = 1L;
10 |
11 | private static final String TABLE_PREFIX = "t_";
12 |
13 | private static final String COLUMN_PREFIX = "c_";
14 |
15 | @Override
16 | public String classToTableName(String className) {
17 | return TABLE_PREFIX + super.classToTableName(className);
18 | }
19 |
20 | @Override
21 | public String collectionTableName(String ownerEntity, String ownerEntityTable, String associatedEntity,
22 | String associatedEntityTable, String propertyName) {
23 | if (!Strings.isNullOrEmpty(associatedEntity) && !Strings.isNullOrEmpty(associatedEntityTable)) {
24 | return TABLE_PREFIX + tableName(ownerEntityTable) + "_" + super.tableName(associatedEntityTable);
25 | } else {
26 | return TABLE_PREFIX + tableName(ownerEntityTable) + "_" + super.propertyToColumnName(propertyName);
27 | }
28 | }
29 |
30 | @Override
31 | public String propertyToColumnName(String propertyName) {
32 | return COLUMN_PREFIX + super.propertyToColumnName(propertyName);
33 | }
34 |
35 | @Override
36 | public String joinKeyColumnName(String joinedColumn, String joinedTable) {
37 | return COLUMN_PREFIX + super.joinKeyColumnName(joinedColumn, joinedTable);
38 | }
39 |
40 | @Override
41 | public String columnName(String columnName) {
42 | return COLUMN_PREFIX + super.columnName(columnName);
43 | }
44 | }
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/infrastructure/jpa/repositories/GenericJpaRepository.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.infrastructure.jpa.repositories;
2 |
3 | import static java.util.Objects.requireNonNull;
4 |
5 | import java.lang.reflect.ParameterizedType;
6 |
7 | import javax.persistence.EntityManager;
8 | import javax.persistence.LockModeType;
9 | import javax.persistence.PersistenceContext;
10 |
11 | import org.springframework.orm.ObjectRetrievalFailureException;
12 |
13 | import example.ddd.AggregateRoot;
14 | import example.ddd.Repository;
15 |
16 | public class GenericJpaRepository, K> implements Repository {
17 |
18 | @PersistenceContext
19 | private EntityManager entityManager;
20 |
21 | private Class entityClass;
22 |
23 | @SuppressWarnings("unchecked")
24 | public GenericJpaRepository() {
25 | this.entityClass = ((Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
26 | }
27 |
28 | @Override
29 | public E load(K id) {
30 | requireNonNull(id);
31 |
32 | E entity = entityManager.find(entityClass, id, LockModeType.OPTIMISTIC);
33 |
34 | if (entity == null) {
35 | throw new ObjectRetrievalFailureException(entityClass, id);
36 | }
37 |
38 | return entity;
39 | }
40 |
41 | @Override
42 | public void save(E entity) {
43 | requireNonNull(entity);
44 |
45 | if (entityManager.contains(entity)) {
46 | entityManager.lock(entity, LockModeType.OPTIMISTIC_FORCE_INCREMENT);
47 | } else {
48 | entityManager.persist(entity);
49 | }
50 |
51 | entityManager.flush();
52 | }
53 |
54 | @Override
55 | public void delete(E entity) {
56 | requireNonNull(entity);
57 |
58 | entityManager.remove(entity);
59 | entityManager.flush();
60 | }
61 |
62 | protected Class getEntityClass() {
63 | return entityClass;
64 | }
65 |
66 | protected EntityManager getEntityManager() {
67 | return entityManager;
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/infrastructure/jpa/repositories/JpaBacklogItemRepository.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.infrastructure.jpa.repositories;
2 |
3 | import example.scrumboard.domain.backlogitem.BacklogItem;
4 | import example.scrumboard.domain.backlogitem.BacklogItemId;
5 | import example.scrumboard.domain.backlogitem.BacklogItemRepository;
6 |
7 | @JpaRepository
8 | public class JpaBacklogItemRepository extends GenericJpaRepository implements
9 | BacklogItemRepository {
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/infrastructure/jpa/repositories/JpaProductRepository.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.infrastructure.jpa.repositories;
2 |
3 | import example.scrumboard.domain.product.Product;
4 | import example.scrumboard.domain.product.ProductId;
5 | import example.scrumboard.domain.product.ProductRepository;
6 |
7 | @JpaRepository
8 | public class JpaProductRepository extends GenericJpaRepository implements ProductRepository {
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/infrastructure/jpa/repositories/JpaReleaseRepository.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.infrastructure.jpa.repositories;
2 |
3 | import example.scrumboard.domain.release.Release;
4 | import example.scrumboard.domain.release.ReleaseId;
5 | import example.scrumboard.domain.release.ReleaseRepository;
6 |
7 | @JpaRepository
8 | public class JpaReleaseRepository extends GenericJpaRepository implements ReleaseRepository {
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/infrastructure/jpa/repositories/JpaRepository.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.infrastructure.jpa.repositories;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | import org.springframework.stereotype.Repository;
9 | import org.springframework.transaction.annotation.Propagation;
10 | import org.springframework.transaction.annotation.Transactional;
11 |
12 | @Repository
13 | @Transactional(readOnly = false, propagation = Propagation.MANDATORY)
14 | @Target({ ElementType.TYPE, ElementType.METHOD })
15 | @Retention(RetentionPolicy.RUNTIME)
16 | public @interface JpaRepository {
17 | }
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/infrastructure/jpa/repositories/JpaSprintRepository.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.infrastructure.jpa.repositories;
2 |
3 | import example.scrumboard.domain.sprint.Sprint;
4 | import example.scrumboard.domain.sprint.SprintId;
5 | import example.scrumboard.domain.sprint.SprintRepository;
6 |
7 | @JpaRepository
8 | public class JpaSprintRepository extends GenericJpaRepository implements SprintRepository {
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/infrastructure/jpa/repositories/JpaTaskRepository.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.infrastructure.jpa.repositories;
2 |
3 | import example.scrumboard.domain.backlogitem.task.Task;
4 | import example.scrumboard.domain.backlogitem.task.TaskId;
5 | import example.scrumboard.domain.backlogitem.task.TaskRepository;
6 |
7 | @JpaRepository
8 | public class JpaTaskRepository extends GenericJpaRepository implements TaskRepository {
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/infrastructure/jpa/spring/EventsPublishingAspect.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.infrastructure.jpa.spring;
2 |
3 | import org.aspectj.lang.annotation.AfterReturning;
4 | import org.aspectj.lang.annotation.Aspect;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.stereotype.Component;
7 |
8 | import example.ddd.AggregateRoot;
9 | import example.ddd.Event;
10 | import example.ddd.EventPublisher;
11 |
12 | @Component
13 | @Aspect
14 | public class EventsPublishingAspect {
15 |
16 | @Autowired
17 | private EventPublisher eventPublisher;
18 |
19 | @AfterReturning(pointcut = "execution(public * example.ddd.Repository+.save(..)) && args(aggregateRoot)")
20 | public void publishPendingEvents(AggregateRoot> aggregateRoot) {
21 | for (Event event : aggregateRoot.getPendingEvents()) {
22 | eventPublisher.publish(event);
23 | }
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/infrastructure/jpa/spring/RepositoryAutowiringAspect.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.infrastructure.jpa.spring;
2 |
3 | import org.aspectj.lang.annotation.AfterReturning;
4 | import org.aspectj.lang.annotation.Aspect;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
7 | import org.springframework.stereotype.Component;
8 |
9 | @Component
10 | @Aspect
11 | public class RepositoryAutowiringAspect {
12 |
13 | @Autowired
14 | private AutowireCapableBeanFactory beanFactory;
15 |
16 | @AfterReturning(pointcut = "execution(public * example.ddd.Repository+.load(..))", returning = "aggregateRoot")
17 | public void autowireLoadedEntity(Object aggregateRoot) {
18 | beanFactory.autowireBean(aggregateRoot);
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/infrastructure/rest/ProductIdConverter.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.infrastructure.rest;
2 |
3 | import org.springframework.core.convert.converter.Converter;
4 | import org.springframework.stereotype.Component;
5 |
6 | import example.scrumboard.domain.product.ProductId;
7 |
8 | @Component
9 | public class ProductIdConverter implements Converter {
10 |
11 | @Override
12 | public ProductId convert(String source) {
13 | return new ProductId(source);
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/infrastructure/rest/ScrumBoardInfrastructureRestConfig.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.infrastructure.rest;
2 |
3 | import java.util.List;
4 |
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.context.annotation.ComponentScan;
7 | import org.springframework.context.annotation.Configuration;
8 | import org.springframework.data.web.config.EnableSpringDataWebSupport;
9 | import org.springframework.http.converter.HttpMessageConverter;
10 | import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
11 | import org.springframework.web.servlet.config.annotation.EnableWebMvc;
12 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
13 |
14 | import com.fasterxml.jackson.annotation.JsonInclude.Include;
15 | import com.fasterxml.jackson.databind.ObjectMapper;
16 |
17 | @Configuration
18 | @ComponentScan
19 | @EnableWebMvc
20 | @EnableSpringDataWebSupport
21 | public class ScrumBoardInfrastructureRestConfig extends WebMvcConfigurerAdapter {
22 |
23 | @Bean
24 | public ObjectMapper objectMapper() {
25 | ObjectMapper objectMapper = new ObjectMapper();
26 |
27 | objectMapper.setSerializationInclusion(Include.NON_NULL);
28 |
29 | return objectMapper;
30 | }
31 |
32 | @Bean
33 | public MappingJackson2HttpMessageConverter mappingJacksonMessageConverter() {
34 | MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
35 | converter.setObjectMapper(objectMapper());
36 | return converter;
37 | }
38 |
39 | @Override
40 | public void configureMessageConverters(List> converters) {
41 | converters.add(mappingJacksonMessageConverter());
42 | }
43 | }
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/infrastructure/shared/ScrumBoardInfrastructureAsyncConfig.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.infrastructure.shared;
2 |
3 | import java.util.concurrent.Executor;
4 |
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.beans.factory.annotation.Qualifier;
7 | import org.springframework.context.annotation.Bean;
8 | import org.springframework.context.annotation.Configuration;
9 | import org.springframework.context.annotation.Profile;
10 | import org.springframework.core.env.Environment;
11 | import org.springframework.scheduling.annotation.AsyncConfigurer;
12 | import org.springframework.scheduling.annotation.EnableAsync;
13 | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
14 |
15 | import example.scrumboard.config.ScrumBoardConfig;
16 |
17 | @Configuration
18 | @Profile({ ScrumBoardConfig.Local.PROFILE, ScrumBoardConfig.Remote.PROFILE })
19 | @EnableAsync
20 | public class ScrumBoardInfrastructureAsyncConfig implements AsyncConfigurer {
21 |
22 | @Autowired
23 | private Environment environment;
24 |
25 | @Override
26 | public Executor getAsyncExecutor() {
27 | return asyncExecutor();
28 | }
29 |
30 | @Bean
31 | @Qualifier("asyncExecutor")
32 | public Executor asyncExecutor() {
33 | ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
34 | executor.setCorePoolSize(environment.getRequiredProperty("scheduling.asyncExecutorCorePoolSize", Integer.class));
35 | executor.setMaxPoolSize(environment.getRequiredProperty("scheduling.asyncExecutorMaxPoolSize", Integer.class));
36 | executor.setQueueCapacity(environment.getRequiredProperty("scheduling.asyncQueueCapacity", Integer.class));
37 | executor.setThreadNamePrefix("ExampleAsyncExecutor-");
38 | return executor;
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/infrastructure/shared/ScrumBoardInfrastructureContextConfig.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.infrastructure.shared;
2 |
3 | import org.springframework.context.annotation.Configuration;
4 | import org.springframework.context.annotation.EnableAspectJAutoProxy;
5 |
6 | @Configuration
7 | @EnableAspectJAutoProxy
8 | public class ScrumBoardInfrastructureContextConfig {
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/infrastructure/shared/ScrumBoardInfrastructureTaskConfig.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.infrastructure.shared;
2 |
3 | import java.util.concurrent.Executor;
4 | import java.util.concurrent.Executors;
5 | import java.util.concurrent.ThreadFactory;
6 |
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.beans.factory.annotation.Qualifier;
9 | import org.springframework.context.annotation.Bean;
10 | import org.springframework.context.annotation.Configuration;
11 | import org.springframework.context.annotation.Profile;
12 | import org.springframework.core.env.Environment;
13 | import org.springframework.scheduling.annotation.EnableScheduling;
14 | import org.springframework.scheduling.annotation.SchedulingConfigurer;
15 | import org.springframework.scheduling.config.ScheduledTaskRegistrar;
16 |
17 | import com.google.common.util.concurrent.ThreadFactoryBuilder;
18 |
19 | import example.scrumboard.config.ScrumBoardConfig;
20 |
21 | @Configuration
22 | @Profile({ ScrumBoardConfig.Local.PROFILE, ScrumBoardConfig.Remote.PROFILE })
23 | @EnableScheduling
24 | public class ScrumBoardInfrastructureTaskConfig implements SchedulingConfigurer {
25 |
26 | @Autowired
27 | private Environment environment;
28 |
29 | @Override
30 | public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
31 | taskRegistrar.setScheduler(taskExecutor());
32 | }
33 |
34 | @Bean(destroyMethod = "shutdown")
35 | @Qualifier("taskExecutor")
36 | public Executor taskExecutor() {
37 | ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("ExampleTaskExecutor-%d").build();
38 | return Executors.newScheduledThreadPool(
39 | environment.getRequiredProperty("scheduling.taskExecutorCorePoolSize", Integer.class), threadFactory);
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/infrastructure/shared/ScrumBoardInfrastructureTransactionConfig.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.infrastructure.shared;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.context.annotation.Bean;
5 | import org.springframework.context.annotation.Configuration;
6 | import org.springframework.orm.jpa.JpaTransactionManager;
7 | import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
8 | import org.springframework.transaction.PlatformTransactionManager;
9 | import org.springframework.transaction.annotation.EnableTransactionManagement;
10 | import org.springframework.transaction.annotation.TransactionManagementConfigurer;
11 |
12 | @Configuration
13 | @EnableTransactionManagement
14 | public class ScrumBoardInfrastructureTransactionConfig implements TransactionManagementConfigurer {
15 |
16 | @Autowired
17 | private LocalContainerEntityManagerFactoryBean entityManagerFactoryBean;
18 |
19 | @Override
20 | public PlatformTransactionManager annotationDrivenTransactionManager() {
21 | return transactionManager();
22 | }
23 |
24 | @Bean
25 | @Autowired
26 | public PlatformTransactionManager transactionManager() {
27 | JpaTransactionManager transactionManager = new JpaTransactionManager();
28 |
29 | transactionManager.setEntityManagerFactory(entityManagerFactoryBean.getObject());
30 |
31 | return transactionManager;
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/rest/commands/ScrumBoardRestCommandsConfig.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.rest.commands;
2 |
3 | import org.springframework.context.annotation.ComponentScan;
4 | import org.springframework.context.annotation.Configuration;
5 |
6 | @Configuration
7 | @ComponentScan
8 | public class ScrumBoardRestCommandsConfig {
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/rest/commands/product/ProductCommandController.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.rest.commands.product;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.http.HttpStatus;
5 | import org.springframework.web.bind.annotation.PathVariable;
6 | import org.springframework.web.bind.annotation.RequestBody;
7 | import org.springframework.web.bind.annotation.RequestMapping;
8 | import org.springframework.web.bind.annotation.RequestMethod;
9 | import org.springframework.web.bind.annotation.ResponseStatus;
10 | import org.springframework.web.bind.annotation.RestController;
11 |
12 | import example.scrumboard.application.api.ProductService;
13 | import example.scrumboard.application.api.commands.CreateProductCommand;
14 | import example.scrumboard.application.api.commands.PlanBacklogItemCommand;
15 | import example.scrumboard.application.api.commands.ReorderBacklogItemsCommand;
16 | import example.scrumboard.domain.backlogitem.BacklogItemId;
17 | import example.scrumboard.domain.product.ProductId;
18 |
19 | @RestController
20 | public class ProductCommandController {
21 |
22 | @Autowired
23 | private ProductService productService;
24 |
25 | @RequestMapping(value = "/products", method = RequestMethod.POST)
26 | @ResponseStatus(HttpStatus.CREATED)
27 | public ProductId create(@RequestBody CreateProductCommand command) {
28 | return productService.createProduct(command);
29 | }
30 |
31 | @RequestMapping(value = "/products/{productId}/backlogItems", method = RequestMethod.POST)
32 | @ResponseStatus(HttpStatus.CREATED)
33 | public BacklogItemId planBacklogItem(@PathVariable("productId") ProductId productId,
34 | @RequestBody PlanBacklogItemCommand command) {
35 | return productService.planBacklogItem(productId, command);
36 | }
37 |
38 | @RequestMapping(value = "/products/{productId}/reorderBacklogItems", method = RequestMethod.POST)
39 | public void reorderBacklogItems(@PathVariable("productId") ProductId productId,
40 | @RequestBody ReorderBacklogItemsCommand command) {
41 | productService.reorderBacklogItems(productId, command);
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/rest/queries/ScrumBoardRestQueriesConfig.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.rest.queries;
2 |
3 | import groovy.sql.Sql;
4 |
5 | import javax.sql.DataSource;
6 |
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.context.annotation.Bean;
9 | import org.springframework.context.annotation.ComponentScan;
10 | import org.springframework.context.annotation.Configuration;
11 |
12 | @Configuration
13 | @ComponentScan
14 | public class ScrumBoardRestQueriesConfig {
15 |
16 | @Bean(destroyMethod = "close")
17 | @Autowired
18 | public Sql sql(DataSource dataSource) {
19 | return new Sql(dataSource);
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/rest/queries/product/ProductQueryController.groovy:
--------------------------------------------------------------------------------
1 | package example.scrumboard.rest.queries.product
2 |
3 | import org.springframework.beans.factory.annotation.Autowired
4 | import org.springframework.data.domain.PageImpl
5 | import org.springframework.data.domain.Pageable
6 | import org.springframework.web.bind.annotation.PathVariable
7 | import org.springframework.web.bind.annotation.RequestMapping
8 | import org.springframework.web.bind.annotation.RequestMethod
9 | import org.springframework.web.bind.annotation.RestController
10 |
11 | import example.scrumboard.domain.product.ProductId
12 | import example.scrumboard.rest.queries.product.dtos.ProductBacklogItemDto
13 | import example.scrumboard.rest.queries.product.dtos.ProductDto
14 | import groovy.sql.Sql
15 |
16 |
17 | @RestController
18 | class ProductQueryController {
19 |
20 | @Autowired
21 | Sql sql
22 |
23 | @RequestMapping(value = "/products", method = RequestMethod.GET)
24 | def products(Pageable page) {
25 | new PageImpl(productsContent(page), page, productsCount())
26 | }
27 |
28 | @RequestMapping(value = "/products/{productId}", method = RequestMethod.GET)
29 | def product(@PathVariable("productId") ProductId productId) {
30 | def query = """
31 | SELECT
32 | p.c_product_id,
33 | p.c_name,
34 | COUNT(pbi.c_product_id) count
35 | FROM
36 | t_product p
37 | LEFT OUTER JOIN t_product_backlog_item pbi ON
38 | p.c_product_id = pbi.c_product_id
39 | WHERE
40 | p.c_product_id = ?
41 | GROUP BY
42 | p.c_product_id, p.c_name
43 | """
44 |
45 | sql.rows(query, [productId.id], 0, 1).collect { row ->
46 | new ProductDto(
47 | productId: row.c_product_id,
48 | productName: row.c_name,
49 | backlogItemsCount: row.count
50 | )
51 | }.first()
52 | }
53 |
54 | @RequestMapping(value = "/products/{productId}/backlogItems", method = RequestMethod.GET)
55 | def backlogItems(@PathVariable("productId") ProductId productId) {
56 | def query = """
57 | SELECT
58 | bi.c_backlog_item_id,
59 | bi.c_story,
60 | pbi.c_position
61 | FROM
62 | t_product_backlog_item pbi
63 | RIGHT OUTER JOIN t_product p ON
64 | pbi.c_product_id = p.c_product_id
65 | RIGHT OUTER JOIN t_backlog_item bi ON
66 | pbi.c_backlog_item_id = bi.c_backlog_item_id
67 | WHERE
68 | p.c_product_id = ?
69 | ORDER BY pbi.c_position
70 | """
71 |
72 | sql.rows(query, [productId.id]).collect { row ->
73 | new ProductBacklogItemDto(
74 | backlogItemId: row.c_backlog_item_id,
75 | backlogItemStory: row.c_story,
76 | backlogItemPosition: row.c_position
77 | )
78 | }
79 | }
80 |
81 | private List productsContent(Pageable page) {
82 | def query = """
83 | SELECT
84 | p.c_product_id,
85 | p.c_name,
86 | COUNT(pbi.c_product_id) count
87 | FROM
88 | t_product p
89 | LEFT OUTER JOIN t_product_backlog_item pbi ON
90 | p.c_product_id = pbi.c_product_id
91 | GROUP BY
92 | p.c_product_id,
93 | p.c_name
94 | ORDER BY
95 | p.c_name
96 | """
97 |
98 | sql.rows(query, page.offset, page.pageSize).collect { row ->
99 | new ProductDto(
100 | productId: row.c_product_id,
101 | productName: row.c_name,
102 | backlogItemsCount: row.count
103 | )
104 | }
105 | }
106 |
107 | private Long productsCount() {
108 | def query = """
109 | SELECT
110 | count(*) count
111 | FROM
112 | t_product
113 | """
114 |
115 | sql.firstRow(query).count
116 | }
117 | }
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/rest/queries/product/dtos/ProductBacklogItemDto.groovy:
--------------------------------------------------------------------------------
1 | package example.scrumboard.rest.queries.product.dtos
2 |
3 | import groovy.transform.Immutable
4 |
5 | @Immutable
6 | class ProductBacklogItemDto {
7 | String backlogItemId
8 | String backlogItemStory
9 | int backlogItemPosition
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/example/scrumboard/rest/queries/product/dtos/ProductDto.groovy:
--------------------------------------------------------------------------------
1 | package example.scrumboard.rest.queries.product.dtos
2 |
3 | import groovy.transform.Immutable
4 |
5 | @Immutable
6 | class ProductDto {
7 | String productId
8 | String productName
9 | int backlogItemsCount
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/resources/local.properties:
--------------------------------------------------------------------------------
1 | jpa.generateDdl=true
2 | jpa.showSql=false
3 |
4 | scheduling.asyncExecutorCorePoolSize=5
5 | scheduling.asyncExecutorMaxPoolSize=10
6 | scheduling.asyncQueueCapacity=100
7 | scheduling.taskExecutorCorePoolSize=3
8 |
--------------------------------------------------------------------------------
/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/main/resources/remote.properties:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mkuthan/example-ddd-cqrs-server/3f4b0136bf816bd5d358c6cc3dfd9501fac78084/src/main/resources/remote.properties
--------------------------------------------------------------------------------
/src/main/webapp/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Insert title here
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/test/java/example/ddd/domain/AggregateRootAssert.java:
--------------------------------------------------------------------------------
1 | package example.ddd.domain;
2 |
3 | import static org.assertj.core.api.Assertions.assertThat;
4 |
5 | import org.assertj.core.api.AbstractAssert;
6 |
7 | import example.ddd.AggregateRoot;
8 | import example.ddd.Event;
9 |
10 | public class AggregateRootAssert extends AbstractAssert> {
11 |
12 | public AggregateRootAssert(AggregateRoot> actual) {
13 | super(actual, AggregateRootAssert.class);
14 | }
15 |
16 | public AggregateRootAssert published(Event event) {
17 | assertThat(actual.getPendingEvents()).contains(event);
18 | return this;
19 | }
20 |
21 | public AggregateRootAssert notPublished() {
22 | assertThat(actual.getPendingEvents()).isEmpty();
23 | return this;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/test/java/example/ddd/domain/DddAssertions.java:
--------------------------------------------------------------------------------
1 | package example.ddd.domain;
2 |
3 | import example.ddd.AggregateRoot;
4 |
5 | public class DddAssertions {
6 |
7 | public static AggregateRootAssert assertThat(AggregateRoot> actual) {
8 | return new AggregateRootAssert(actual);
9 | }
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/test/java/example/scrumboard/TestGroups.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard;
2 |
3 | public interface TestGroups {
4 |
5 | String UNIT = "unit";
6 | String INTEGRATION = "integration";
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/src/test/java/example/scrumboard/domain/ScrumBoardBuilders.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain;
2 |
3 | import example.scrumboard.domain.backlogitem.BacklogItemBuilder;
4 | import example.scrumboard.domain.sprint.SprintBuilder;
5 |
6 | public class ScrumBoardBuilders {
7 |
8 | public static BacklogItemBuilder givenBacklogItem() {
9 | return new BacklogItemBuilder();
10 | }
11 |
12 | public static SprintBuilder givenSprint() {
13 | return new SprintBuilder();
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/test/java/example/scrumboard/domain/backlogitem/BacklogItemAssert.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.backlogitem;
2 |
3 | import static example.ddd.domain.DddAssertions.assertThat;
4 | import static org.assertj.core.api.Assertions.assertThat;
5 |
6 | import org.assertj.core.api.AbstractAssert;
7 |
8 | import example.scrumboard.domain.sprint.SprintId;
9 |
10 | public class BacklogItemAssert extends AbstractAssert {
11 |
12 | public BacklogItemAssert(BacklogItem actual) {
13 | super(actual, BacklogItemAssert.class);
14 | }
15 |
16 | public BacklogItemAssert isCommitedToSprint(SprintId sprintId) {
17 | assertThat(actual.getSprintId()).isEqualTo(sprintId);
18 | return this;
19 | }
20 |
21 | public BacklogItemAssert isNotCommited() {
22 | assertThat(actual.getSprintId()).isNull();
23 | return this;
24 | }
25 |
26 | public BacklogItemAssert backlogItemCommitedPublished(SprintId sprintId) {
27 | assertThat(actual).published(new BacklogItemCommited(actual.getId(), sprintId));
28 | return this;
29 | }
30 |
31 | public BacklogItemAssert backlogItemUncommitedPublished(SprintId sprintId) {
32 | assertThat(actual).published(new BacklogItemUncommited(actual.getId(), sprintId));
33 | return this;
34 | }
35 |
36 | public BacklogItemAssert eventNotPublished() {
37 | assertThat(actual).notPublished();
38 | return this;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/test/java/example/scrumboard/domain/backlogitem/BacklogItemBuilder.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.backlogitem;
2 |
3 | import example.scrumboard.domain.product.Product;
4 | import example.scrumboard.domain.product.ProductBuilder;
5 | import example.scrumboard.domain.sprint.Sprint;
6 |
7 | public class BacklogItemBuilder {
8 |
9 | private BacklogItemId id = new BacklogItemId("any id");
10 |
11 | private Product product = new ProductBuilder().build();
12 |
13 | private String name = "any name";
14 |
15 | private Sprint sprint;
16 |
17 | public BacklogItemBuilder withId(BacklogItemId id) {
18 | this.id = id;
19 | return this;
20 | }
21 |
22 | public BacklogItemBuilder withProduct(Product product) {
23 | this.product = product;
24 | return this;
25 | }
26 |
27 | public BacklogItemBuilder withName(String name) {
28 | this.name = name;
29 | return this;
30 | }
31 |
32 | public BacklogItemBuilder commitedToSprint(Sprint sprint) {
33 | this.sprint = sprint;
34 | return this;
35 | }
36 |
37 | public BacklogItem build() {
38 | return new BacklogItem(id, product, name, sprint);
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/test/java/example/scrumboard/domain/backlogitem/BacklogItemTest.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.backlogitem;
2 |
3 | import static com.googlecode.catchexception.CatchException.catchException;
4 | import static com.googlecode.catchexception.CatchException.caughtException;
5 | import static example.scrumboard.domain.ScrumBoardBuilders.givenSprint;
6 | import static org.assertj.core.api.Assertions.assertThat;
7 |
8 | import org.testng.annotations.Test;
9 |
10 | import example.scrumboard.TestGroups;
11 | import example.scrumboard.domain.sprint.Sprint;
12 | import example.scrumboard.domain.sprint.SprintId;
13 |
14 | @Test(groups = TestGroups.UNIT)
15 | public class BacklogItemTest {
16 |
17 | private BacklogItem backlogItem;
18 |
19 | private BacklogItemBuilder backlogItemBuilder;
20 |
21 | public void shouldCommitToSprint() {
22 | SprintId sprintId = new SprintId("id");
23 | Sprint sprint = givenSprint().withId(sprintId).build();
24 |
25 | BacklogItemId backlogItemId = new BacklogItemId("id");
26 | givenBacklogItem().withId(backlogItemId);
27 |
28 | whenBacklogItem().commitToSprint(sprint);
29 |
30 | // @formatter:off
31 | thenBacklogItem()
32 | .isCommitedToSprint(sprintId)
33 | .backlogItemCommitedPublished(sprintId);
34 | // @formatter:on
35 | }
36 |
37 | public void shouldNotCommitToSprintWhenNoChanges() {
38 | SprintId sprintId = new SprintId("id");
39 | Sprint sprint = givenSprint().withId(sprintId).build();
40 |
41 | BacklogItemId backlogItemId = new BacklogItemId("id");
42 | givenBacklogItem().withId(backlogItemId).commitedToSprint(sprint);
43 |
44 | whenBacklogItem().commitToSprint(sprint);
45 |
46 | // @formatter:off
47 | thenBacklogItem()
48 | .isCommitedToSprint(sprint.getId())
49 | .eventNotPublished();
50 | // @formatter:on
51 | }
52 |
53 | public void shouldCommitToSprintWhenCommited() {
54 | SprintId oldSprintId = new SprintId("old");
55 | Sprint oldSprint = givenSprint().withId(oldSprintId).build();
56 |
57 | SprintId newSprintId = new SprintId("new");
58 | Sprint newSprint = givenSprint().withId(newSprintId).build();
59 |
60 | BacklogItemId backlogItemId = new BacklogItemId("id");
61 | givenBacklogItem().withId(backlogItemId).commitedToSprint(oldSprint);
62 |
63 | whenBacklogItem().commitToSprint(newSprint);
64 |
65 | // @formatter:off
66 | thenBacklogItem()
67 | .isCommitedToSprint(newSprintId)
68 | .backlogItemUncommitedPublished(oldSprintId)
69 | .backlogItemCommitedPublished(newSprintId);
70 | // @formatter:on
71 | }
72 |
73 | public void shouldUncommitFromSprint() {
74 | SprintId sprintId = new SprintId("id");
75 | Sprint sprint = givenSprint().withId(sprintId).build();
76 |
77 | BacklogItemId backlogItemId = new BacklogItemId("id");
78 | givenBacklogItem().withId(backlogItemId).commitedToSprint(sprint);
79 |
80 | whenBacklogItem().uncommitFromSprint(sprint);
81 |
82 | // @formatter:off
83 | thenBacklogItem()
84 | .isNotCommited()
85 | .backlogItemUncommitedPublished(sprintId);
86 | // @formatter:on
87 | }
88 |
89 | public void shouldNotUncommitFromSprintWhenNotCommited() {
90 | SprintId sprintId = new SprintId("id");
91 | Sprint sprint = givenSprint().withId(sprintId).build();
92 |
93 | BacklogItemId backlogItemId = new BacklogItemId("id");
94 | givenBacklogItem().withId(backlogItemId);
95 |
96 | catchException(whenBacklogItem()).uncommitFromSprint(sprint);
97 |
98 | assertThat(caughtException()).isInstanceOf(IllegalArgumentException.class);
99 | thenBacklogItem().eventNotPublished();
100 | }
101 |
102 | public void shouldNotUncommitFromSprintWhenNotCommitedToGivenSprint() {
103 | SprintId oldSprintId = new SprintId("old");
104 | Sprint oldSprint = givenSprint().withId(oldSprintId).build();
105 |
106 | SprintId newSprintId = new SprintId("new");
107 | Sprint newSprint = givenSprint().withId(newSprintId).build();
108 |
109 | BacklogItemId backlogItemId = new BacklogItemId("id");
110 | givenBacklogItem().withId(backlogItemId).commitedToSprint(oldSprint);
111 |
112 | catchException(whenBacklogItem()).uncommitFromSprint(newSprint);
113 |
114 | assertThat(caughtException()).isInstanceOf(IllegalArgumentException.class);
115 | thenBacklogItem().eventNotPublished();
116 | }
117 |
118 | private BacklogItemBuilder givenBacklogItem() {
119 | backlogItemBuilder = new BacklogItemBuilder();
120 | return backlogItemBuilder;
121 | }
122 |
123 | private BacklogItem whenBacklogItem() {
124 | backlogItem = backlogItemBuilder.build();
125 | return backlogItem;
126 | }
127 |
128 | private BacklogItemAssert thenBacklogItem() {
129 | return new BacklogItemAssert(backlogItem);
130 | }
131 |
132 | }
133 |
--------------------------------------------------------------------------------
/src/test/java/example/scrumboard/domain/product/ProductAssert.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.product;
2 |
3 | import static example.ddd.domain.DddAssertions.assertThat;
4 | import static org.assertj.core.api.Assertions.assertThat;
5 |
6 | import org.assertj.core.api.AbstractAssert;
7 |
8 | import com.google.common.base.Optional;
9 | import com.google.common.collect.Iterables;
10 |
11 | import example.scrumboard.domain.backlogitem.BacklogItemId;
12 |
13 | public class ProductAssert extends AbstractAssert {
14 |
15 | public ProductAssert(Product actual) {
16 | super(actual, ProductAssert.class);
17 | }
18 |
19 | public ProductAssert hasName(String name) {
20 | assertThat(actual.getName()).isEqualTo(name);
21 | return this;
22 | }
23 |
24 | public ProductAssert hasBacklogItem(BacklogItemId backlogItemId, int position) {
25 | Optional backlogItem = Iterables.tryFind(actual.getBacklogItems(),
26 | ProductBacklogItem.hasId(backlogItemId));
27 | assertThat(backlogItem.orNull()).overridingErrorMessage("Backlog item %s not found", backlogItemId).isNotNull();
28 |
29 | int actualPosition = backlogItem.get().getPosition();
30 | assertThat(actualPosition).overridingErrorMessage(
31 | "Expected backlog item '%s' at position %s, but was at position %s", backlogItemId, position,
32 | actualPosition).isEqualTo(position);
33 |
34 | return this;
35 | }
36 |
37 | public ProductAssert backlogItemPlannedEventPublished(BacklogItemId backlogItemId) {
38 | assertThat(actual).published(new BacklogItemPlannedEvent(actual.getId(), backlogItemId));
39 | return this;
40 | }
41 |
42 | public ProductAssert eventNotPublished() {
43 | assertThat(actual).notPublished();
44 | return this;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/test/java/example/scrumboard/domain/product/ProductBuilder.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.product;
2 |
3 | import java.util.ArrayList;
4 | import java.util.HashSet;
5 | import java.util.List;
6 |
7 | import example.scrumboard.domain.backlogitem.BacklogItemId;
8 |
9 | public class ProductBuilder {
10 |
11 | private ProductId id = new ProductId("any id");
12 |
13 | private String name = "any name";
14 |
15 | private List backlogItems = new ArrayList<>();
16 |
17 | public ProductBuilder withId(ProductId id) {
18 | this.id = id;
19 | return this;
20 | }
21 |
22 | public ProductBuilder withName(String name) {
23 | this.name = name;
24 | return this;
25 | }
26 |
27 | public ProductBuilder addBacklogItem(BacklogItemId id) {
28 | int position = backlogItems.size();
29 | backlogItems.add(new ProductBacklogItem(id, position));
30 | return this;
31 | }
32 |
33 | public Product build() {
34 | return new Product(id, name, new HashSet<>(backlogItems));
35 | }
36 | }
--------------------------------------------------------------------------------
/src/test/java/example/scrumboard/domain/product/ProductTest.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.product;
2 |
3 | import static com.googlecode.catchexception.CatchException.catchException;
4 | import static com.googlecode.catchexception.CatchException.caughtException;
5 | import static example.scrumboard.domain.ScrumBoardBuilders.givenBacklogItem;
6 | import static org.assertj.core.api.Assertions.assertThat;
7 |
8 | import org.testng.annotations.Test;
9 |
10 | import example.scrumboard.TestGroups;
11 | import example.scrumboard.domain.backlogitem.BacklogItem;
12 | import example.scrumboard.domain.backlogitem.BacklogItemId;
13 |
14 | @Test(groups = TestGroups.UNIT)
15 | public class ProductTest {
16 |
17 | private Product product;
18 |
19 | private ProductBuilder productBuilder;
20 |
21 | public void shouldAssignFirstBacklogItem() {
22 | BacklogItemId backlogItemId = new BacklogItemId("id");
23 | BacklogItem backlogItem = givenBacklogItem().withId(backlogItemId).build();
24 |
25 | givenProduct();
26 |
27 | whenProduct().planBacklogItem(backlogItem);
28 |
29 | // @formatter:off
30 | thenProduct()
31 | .hasBacklogItem(backlogItemId, 0)
32 | .backlogItemPlannedEventPublished(backlogItemId);
33 | // @formatter:on
34 | }
35 |
36 | public void shouldAssignSecondBacklogItem() {
37 | BacklogItemId backlogItemId = new BacklogItemId("id");
38 | BacklogItem backlogItem = givenBacklogItem().withId(backlogItemId).build();
39 |
40 | givenProduct().addBacklogItem(new BacklogItemId("existing id"));
41 |
42 | whenProduct().planBacklogItem(backlogItem);
43 |
44 | // @formatter:off
45 | thenProduct()
46 | .hasBacklogItem(backlogItemId, 1)
47 | .backlogItemPlannedEventPublished(backlogItemId);
48 | // @formatter:on
49 | }
50 |
51 | public void shouldNotAssignExistingBacklogItem() {
52 | BacklogItemId backlogItemId = new BacklogItemId("id");
53 | BacklogItem backlogItem = givenBacklogItem().withId(backlogItemId).build();
54 |
55 | givenProduct().addBacklogItem(backlogItemId);
56 |
57 | catchException(whenProduct()).planBacklogItem(backlogItem);
58 |
59 | assertThat(caughtException()).isInstanceOf(IllegalArgumentException.class);
60 | thenProduct().eventNotPublished();
61 | }
62 |
63 | public void shouldReorder() {
64 | BacklogItemId backlogItemId0 = new BacklogItemId("0");
65 | BacklogItemId backlogItemId1 = new BacklogItemId("1");
66 | BacklogItemId backlogItemId2 = new BacklogItemId("2");
67 |
68 | // @formatter:off
69 | givenProduct()
70 | .addBacklogItem(backlogItemId0)
71 | .addBacklogItem(backlogItemId1)
72 | .addBacklogItem(backlogItemId2);
73 | // @formatter:on
74 |
75 | whenProduct().reorderBacklogItems(backlogItemId2, backlogItemId1, backlogItemId0);
76 |
77 | // @formatter:off
78 | thenProduct()
79 | .hasBacklogItem(backlogItemId0, 2)
80 | .hasBacklogItem(backlogItemId1, 1)
81 | .hasBacklogItem(backlogItemId2, 0)
82 | .eventNotPublished();
83 | // @formatter:on
84 | }
85 |
86 | public void shouldNotReorderWhenNoChanges() {
87 | BacklogItemId backlogItemId0 = new BacklogItemId("0");
88 | BacklogItemId backlogItemId1 = new BacklogItemId("1");
89 | BacklogItemId backlogItemId2 = new BacklogItemId("2");
90 |
91 | // @formatter:off
92 | givenProduct()
93 | .addBacklogItem(backlogItemId0)
94 | .addBacklogItem(backlogItemId1)
95 | .addBacklogItem(backlogItemId2);
96 | // @formatter:on
97 |
98 | whenProduct().reorderBacklogItems(backlogItemId0, backlogItemId1, backlogItemId2);
99 |
100 | // @formatter:off
101 | thenProduct()
102 | .hasBacklogItem(backlogItemId0, 0)
103 | .hasBacklogItem(backlogItemId1, 1)
104 | .hasBacklogItem(backlogItemId2, 2)
105 | .eventNotPublished();
106 | // @formatter:on
107 | }
108 |
109 | public void shouldNotReorderNonExistingBacklogItem() {
110 | BacklogItemId backlogItemId0 = new BacklogItemId("0");
111 | BacklogItemId backlogItemId1 = new BacklogItemId("1");
112 |
113 | givenProduct().addBacklogItem(backlogItemId0);
114 |
115 | catchException(whenProduct()).reorderBacklogItems(backlogItemId1);
116 | assertThat(caughtException()).isInstanceOf(IllegalArgumentException.class);
117 |
118 | thenProduct().eventNotPublished();
119 | }
120 |
121 | private ProductBuilder givenProduct() {
122 | productBuilder = new ProductBuilder();
123 | return productBuilder;
124 | }
125 |
126 | private Product whenProduct() {
127 | this.product = productBuilder.build();
128 | return product;
129 | }
130 |
131 | private ProductAssert thenProduct() {
132 | return new ProductAssert(product);
133 | }
134 |
135 | }
136 |
--------------------------------------------------------------------------------
/src/test/java/example/scrumboard/domain/sprint/SprintBuilder.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.domain.sprint;
2 |
3 | import java.util.Date;
4 | import java.util.HashSet;
5 | import java.util.Set;
6 |
7 | import org.joda.time.format.DateTimeFormat;
8 | import org.joda.time.format.DateTimeFormatter;
9 |
10 | import example.scrumboard.domain.product.Product;
11 | import example.scrumboard.domain.product.ProductBuilder;
12 |
13 | public class SprintBuilder {
14 |
15 | private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormat.forPattern("YYYY-MM-DD");
16 |
17 | private SprintId id = new SprintId("any id");
18 |
19 | private Product product = new ProductBuilder().build();
20 |
21 | private String name = "any name";
22 |
23 | private Date beginDate = DATE_TIME_FORMATTER.parseDateTime("2010-01-01").toDate();
24 |
25 | private Date endDate = DATE_TIME_FORMATTER.parseDateTime("2010-01-14").toDate();
26 |
27 | private Set backlogItems = new HashSet<>();
28 |
29 | public SprintBuilder withId(SprintId id) {
30 | this.id = id;
31 | return this;
32 | }
33 |
34 | public SprintBuilder withProduct(Product product) {
35 | this.product = product;
36 | return this;
37 | }
38 |
39 | public SprintBuilder withName(String name) {
40 | this.name = name;
41 | return this;
42 | }
43 |
44 | public SprintBuilder withBeginDate(String beginDate) {
45 | this.beginDate = DATE_TIME_FORMATTER.parseDateTime(beginDate).toDate();
46 | return this;
47 | }
48 |
49 | public SprintBuilder withEndDate(String endDate) {
50 | this.endDate = DATE_TIME_FORMATTER.parseDateTime(endDate).toDate();
51 | return this;
52 | }
53 |
54 | public SprintBuilder addBacklogItem(CommitedBacklogItem backlogItem) {
55 | this.backlogItems.add(backlogItem);
56 | return this;
57 | }
58 |
59 | public Sprint build() {
60 | return new Sprint(id, product, name, beginDate, endDate, backlogItems);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/test/java/example/scrumboard/infrastructure/jpa/JpaTest.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.infrastructure.jpa;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | import org.springframework.test.context.ContextConfiguration;
9 |
10 | @ContextConfiguration(classes = JpaTestConfig.class)
11 | @Target(ElementType.TYPE)
12 | @Retention(RetentionPolicy.RUNTIME)
13 | public @interface JpaTest {
14 | }
--------------------------------------------------------------------------------
/src/test/java/example/scrumboard/infrastructure/jpa/JpaTestConfig.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.infrastructure.jpa;
2 |
3 | import javax.sql.DataSource;
4 |
5 | import org.mockito.Mockito;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Import;
8 | import org.springframework.context.annotation.PropertySource;
9 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
10 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
11 |
12 | import example.ddd.EventPublisher;
13 | import example.scrumboard.infrastructure.shared.ScrumBoardInfrastructureTransactionConfig;
14 |
15 | @Import({ ScrumBoardInfrastructureJpaConfig.class, ScrumBoardInfrastructureTransactionConfig.class })
16 | @PropertySource("classpath:/test.properties")
17 | public class JpaTestConfig {
18 |
19 | @Bean
20 | public DataSource dataSource() {
21 | return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2)
22 | .setName(JpaTestConfig.class.getSimpleName()).build();
23 | }
24 |
25 | @Bean
26 | public EventPublisher PublishSubscribeChannel() {
27 | return Mockito.mock(EventPublisher.class);
28 | }
29 |
30 | }
--------------------------------------------------------------------------------
/src/test/java/example/scrumboard/infrastructure/jpa/repositories/AbstractJpaRepositoryTest.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.infrastructure.jpa.repositories;
2 |
3 | import javax.persistence.EntityManager;
4 | import javax.persistence.PersistenceContext;
5 | import javax.persistence.TypedQuery;
6 |
7 | import org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests;
8 | import org.testng.annotations.Test;
9 |
10 | import example.scrumboard.TestGroups;
11 |
12 | @Test(groups = TestGroups.INTEGRATION)
13 | public abstract class AbstractJpaRepositoryTest extends AbstractTransactionalTestNGSpringContextTests {
14 |
15 | @PersistenceContext
16 | private EntityManager entityManager;
17 |
18 | protected void clear() {
19 | entityManager.clear();
20 | }
21 |
22 | protected Long countEntities(Class> entityClass) {
23 | StringBuffer queryString = new StringBuffer("SELECT count(e) from ") //
24 | .append(entityClass.getSimpleName()) //
25 | .append(" e");
26 |
27 | TypedQuery query = entityManager.createQuery(queryString.toString(), Long.class);
28 | return query.getSingleResult();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/test/java/example/scrumboard/infrastructure/jpa/repositories/JpaProductRepositoryTest.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.infrastructure.jpa.repositories;
2 |
3 | import static org.assertj.core.api.Assertions.assertThat;
4 |
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.dao.DataIntegrityViolationException;
7 | import org.testng.annotations.Test;
8 |
9 | import example.scrumboard.domain.backlogitem.BacklogItemId;
10 | import example.scrumboard.domain.product.Product;
11 | import example.scrumboard.domain.product.ProductAssert;
12 | import example.scrumboard.domain.product.ProductBacklogItem;
13 | import example.scrumboard.domain.product.ProductBuilder;
14 | import example.scrumboard.domain.product.ProductId;
15 | import example.scrumboard.domain.product.ProductRepository;
16 | import example.scrumboard.infrastructure.jpa.JpaTest;
17 |
18 | @JpaTest
19 | public class JpaProductRepositoryTest extends AbstractJpaRepositoryTest {
20 |
21 | @Autowired
22 | private ProductRepository repository;
23 |
24 | public void shouldSaveProduct() {
25 | // given
26 | ProductId id = new ProductId("id");
27 | String name = "name";
28 | BacklogItemId backlogItemId1 = new BacklogItemId("id1");
29 | BacklogItemId backlogItemId2 = new BacklogItemId("id2");
30 |
31 | // @formatter:off
32 | Product product = givenProduct()
33 | .withId(id)
34 | .withName(name)
35 | .addBacklogItem(backlogItemId1)
36 | .addBacklogItem(backlogItemId2)
37 | .build();
38 | // @formatter:on
39 |
40 | // when
41 | repository.save(product);
42 |
43 | // @formatter:off
44 | thenProduct(id)
45 | .hasName(name)
46 | .hasBacklogItem(backlogItemId1, 0)
47 | .hasBacklogItem(backlogItemId2, 1);
48 | // @formatter:on
49 | }
50 |
51 | @Test(expectedExceptions = DataIntegrityViolationException.class)
52 | public void shouldNotSaveProductWithDuplicatedName() {
53 | String name = "name";
54 | Product product1 = givenProduct().withId(new ProductId("id1")).withName(name).build();
55 | Product product2 = givenProduct().withId(new ProductId("id2")).withName(name).build();
56 |
57 | // when
58 | repository.save(product1);
59 | repository.save(product2);
60 | }
61 |
62 | public void shouldDeleteProduct() {
63 | ProductId id = new ProductId("id");
64 |
65 | // @formatter:off
66 | Product product = givenProduct()
67 | .withId(id)
68 | .addBacklogItem(new BacklogItemId("id1"))
69 | .addBacklogItem(new BacklogItemId("id2"))
70 | .build();
71 | // @formatter:on
72 |
73 | repository.save(product);
74 | repository.delete(product);
75 |
76 | assertThat(countEntities(Product.class)).isEqualTo(0);
77 | assertThat(countEntities(ProductBacklogItem.class)).isEqualTo(0);
78 | }
79 |
80 | private ProductBuilder givenProduct() {
81 | return new ProductBuilder();
82 | }
83 |
84 | private ProductAssert thenProduct(ProductId id) {
85 | clear();
86 | return new ProductAssert(repository.load(id));
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/test/java/example/scrumboard/rest/AbstractControllerTest.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.rest;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
5 | import org.springframework.test.web.servlet.MockMvc;
6 | import org.springframework.test.web.servlet.setup.MockMvcBuilders;
7 | import org.springframework.web.context.WebApplicationContext;
8 | import org.testng.annotations.BeforeMethod;
9 | import org.testng.annotations.Test;
10 |
11 | import com.fasterxml.jackson.core.JsonProcessingException;
12 | import com.fasterxml.jackson.databind.ObjectMapper;
13 |
14 | import example.scrumboard.TestGroups;
15 |
16 | @Test(groups = TestGroups.INTEGRATION)
17 | public abstract class AbstractControllerTest extends AbstractTestNGSpringContextTests {
18 |
19 | @Autowired
20 | private WebApplicationContext webApplicationContext;
21 |
22 | @Autowired
23 | private ObjectMapper objectMapper;
24 |
25 | private MockMvc mockMvc;
26 |
27 | protected MockMvc getMockMvc() {
28 | return mockMvc;
29 | }
30 |
31 | protected String toJson(Object object) {
32 | try {
33 | return objectMapper.writeValueAsString(object);
34 | } catch (JsonProcessingException e) {
35 | throw new RuntimeException("Could not create JSON representation for " + object + ".", e);
36 | }
37 | }
38 |
39 | @BeforeMethod
40 | protected void setup() {
41 | mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build();
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/test/java/example/scrumboard/rest/commands/RestCommandTest.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.rest.commands;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | import org.springframework.test.context.ContextConfiguration;
9 | import org.springframework.test.context.web.WebAppConfiguration;
10 |
11 | @WebAppConfiguration
12 | @ContextConfiguration(classes = RestCommandTestConfig.class)
13 | @Target(ElementType.TYPE)
14 | @Retention(RetentionPolicy.RUNTIME)
15 | public @interface RestCommandTest {
16 | }
--------------------------------------------------------------------------------
/src/test/java/example/scrumboard/rest/commands/RestCommandTestConfig.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.rest.commands;
2 |
3 | import org.mockito.Mockito;
4 | import org.springframework.context.annotation.Bean;
5 | import org.springframework.context.annotation.Import;
6 | import org.springframework.context.annotation.PropertySource;
7 |
8 | import example.scrumboard.application.api.ProductService;
9 | import example.scrumboard.application.api.ReleaseService;
10 | import example.scrumboard.application.api.SprintService;
11 | import example.scrumboard.infrastructure.rest.ScrumBoardInfrastructureRestConfig;
12 |
13 | @Import({ ScrumBoardInfrastructureRestConfig.class, ScrumBoardRestCommandsConfig.class })
14 | @PropertySource("classpath:/test.properties")
15 | public class RestCommandTestConfig {
16 |
17 | @Bean
18 | public ProductService productService() {
19 | return Mockito.mock(ProductService.class);
20 | }
21 |
22 | @Bean
23 | public ReleaseService releaseService() {
24 | return Mockito.mock(ReleaseService.class);
25 | }
26 |
27 | @Bean
28 | public SprintService sprintService() {
29 | return Mockito.mock(SprintService.class);
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/src/test/java/example/scrumboard/rest/commands/product/ProductCommandControllerTest.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.rest.commands.product;
2 |
3 | import static org.mockito.Matchers.eq;
4 | import static org.mockito.Mockito.verify;
5 | import static org.mockito.Mockito.when;
6 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
7 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
8 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
9 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
10 |
11 | import org.springframework.beans.factory.annotation.Autowired;
12 | import org.springframework.http.MediaType;
13 |
14 | import example.scrumboard.application.api.ProductService;
15 | import example.scrumboard.application.api.commands.CreateProductCommand;
16 | import example.scrumboard.application.api.commands.PlanBacklogItemCommand;
17 | import example.scrumboard.application.api.commands.ReorderBacklogItemsCommand;
18 | import example.scrumboard.domain.backlogitem.BacklogItemId;
19 | import example.scrumboard.domain.product.ProductId;
20 | import example.scrumboard.rest.AbstractControllerTest;
21 | import example.scrumboard.rest.commands.RestCommandTest;
22 |
23 | @RestCommandTest
24 | public class ProductCommandControllerTest extends AbstractControllerTest {
25 |
26 | private static final ProductId ANY_PRODUCT_ID = new ProductId("any product id");
27 |
28 | private static final BacklogItemId ANY_BACKLOG_ITEM_ID = new BacklogItemId("any backlog item id");
29 |
30 | @Autowired
31 | private ProductService productService;
32 |
33 | public void createProduct() throws Exception {
34 | CreateProductCommand command = givenCreateProductCommand();
35 |
36 | when(productService.createProduct(eq(command))).thenReturn(ANY_PRODUCT_ID);
37 |
38 | // @formatter:off
39 | getMockMvc().perform(post("/products")
40 | .content(toJson(command)).contentType(MediaType.APPLICATION_JSON)
41 | .accept(MediaType.APPLICATION_JSON))
42 | //.andDo(print())
43 | .andExpect(status().isCreated())
44 | .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
45 | .andExpect(jsonPath("$id").value(ANY_PRODUCT_ID.getId()));
46 | // @formatter:on
47 | }
48 |
49 | public void planBacklogItem() throws Exception {
50 | PlanBacklogItemCommand command = givenPlanBacklogItemCommand();
51 |
52 | when(productService.planBacklogItem(eq(ANY_PRODUCT_ID), eq(command))).thenReturn(ANY_BACKLOG_ITEM_ID);
53 |
54 | // @formatter:off
55 | getMockMvc().perform(post("/products/{productId}/backlogItems", ANY_PRODUCT_ID.getId())
56 | .content(toJson(command)).contentType(MediaType.APPLICATION_JSON)
57 | .accept(MediaType.APPLICATION_JSON))
58 | //.andDo(print())
59 | .andExpect(status().isCreated())
60 | .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
61 | .andExpect(jsonPath("$id").value(ANY_BACKLOG_ITEM_ID.getId()));
62 | // @formatter:on
63 | }
64 |
65 | public void reorderBacklogItems() throws Exception {
66 | ReorderBacklogItemsCommand command = givenReorderBacklogItemsCommand();
67 |
68 | // @formatter:off
69 | getMockMvc().perform(post("/products/{productId}/reorderBacklogItems", ANY_PRODUCT_ID.getId())
70 | .content(toJson(command)).contentType(MediaType.APPLICATION_JSON)
71 | .accept(MediaType.APPLICATION_JSON))
72 | //.andDo(print())
73 | .andExpect(status().isOk());
74 | // @formatter:on
75 |
76 | verify(productService).reorderBacklogItems(eq(ANY_PRODUCT_ID), eq(command));
77 | }
78 |
79 | private CreateProductCommand givenCreateProductCommand() {
80 | CreateProductCommand command = new CreateProductCommand();
81 | command.setProductName("any product name");
82 | return command;
83 | }
84 |
85 | private PlanBacklogItemCommand givenPlanBacklogItemCommand() {
86 | PlanBacklogItemCommand command = new PlanBacklogItemCommand();
87 | command.setBacklogItemStory("any backlog item story");
88 | return command;
89 | }
90 |
91 | private ReorderBacklogItemsCommand givenReorderBacklogItemsCommand() {
92 | ReorderBacklogItemsCommand command = new ReorderBacklogItemsCommand();
93 | return command;
94 | }
95 |
96 | }
97 |
--------------------------------------------------------------------------------
/src/test/java/example/scrumboard/rest/queries/RestQueryTest.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.rest.queries;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | import org.springframework.test.context.ContextConfiguration;
9 | import org.springframework.test.context.web.WebAppConfiguration;
10 |
11 | @WebAppConfiguration
12 | @ContextConfiguration(classes = RestQueryTestConfig.class)
13 | @Target(ElementType.TYPE)
14 | @Retention(RetentionPolicy.RUNTIME)
15 | public @interface RestQueryTest {
16 | }
17 |
--------------------------------------------------------------------------------
/src/test/java/example/scrumboard/rest/queries/RestQueryTestConfig.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.rest.queries;
2 |
3 | import javax.sql.DataSource;
4 |
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.context.annotation.Import;
7 | import org.springframework.context.annotation.PropertySource;
8 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
9 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
10 |
11 | import example.scrumboard.infrastructure.rest.ScrumBoardInfrastructureRestConfig;
12 |
13 | @Import({ ScrumBoardInfrastructureRestConfig.class, ScrumBoardRestQueriesConfig.class })
14 | @PropertySource("classpath:/test.properties")
15 | public class RestQueryTestConfig {
16 |
17 | @Bean
18 | public DataSource dataSource() {
19 | return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2)
20 | .setName(RestQueryTestConfig.class.getSimpleName()).addScript("sample.sql").build();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/test/java/example/scrumboard/rest/queries/product/ProductQueryControllerTest.java:
--------------------------------------------------------------------------------
1 | package example.scrumboard.rest.queries.product;
2 |
3 | import static com.jayway.jsonpath.Criteria.where;
4 | import static com.jayway.jsonpath.Filter.filter;
5 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
6 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
7 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
8 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
9 |
10 | import org.springframework.http.MediaType;
11 | import org.testng.annotations.Test;
12 |
13 | import com.jayway.jsonpath.JsonPath;
14 |
15 | import example.scrumboard.rest.AbstractControllerTest;
16 | import example.scrumboard.rest.queries.RestQueryTest;
17 |
18 | @RestQueryTest
19 | public class ProductQueryControllerTest extends AbstractControllerTest {
20 |
21 | private String productsJson;
22 |
23 | public void shouldFindProducts() throws Exception {
24 | // @formatter:off
25 | productsJson = getMockMvc().perform(get("/products").accept(MediaType.APPLICATION_JSON))
26 | //.andDo(print())
27 | .andExpect(status().isOk())
28 | .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
29 | .andExpect(jsonPath("$totalElements").value(3))
30 | .andExpect(jsonPath("$content[0].productName").value("Example DDD/CQRS client"))
31 | .andExpect(jsonPath("$content[1].productName").value("Example DDD/CQRS server"))
32 | .andExpect(jsonPath("$content[2].productName").value("Product with no backlog items"))
33 | .andReturn().getResponse().getContentAsString();
34 | // @formatter:on
35 | }
36 |
37 | @Test(dependsOnMethods = "shouldFindProducts")
38 | public void shouldFindProduct() throws Exception {
39 | String productName = "Example DDD/CQRS server";
40 | String productId = findProductIdByName(productName);
41 |
42 | // @formatter:off
43 | getMockMvc().perform(get("/products/{productId}", productId)
44 | .accept(MediaType.APPLICATION_JSON))
45 | //.andDo(print())
46 | .andExpect(status().isOk())
47 | .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
48 | .andExpect(jsonPath("$productId").value(productId))
49 | .andExpect(jsonPath("$productName").value(productName));
50 | // @formatter:on
51 | }
52 |
53 | @Test(dependsOnMethods = "shouldFindProducts")
54 | public void shouldFindBacklogItems() throws Exception {
55 | String productName = "Example DDD/CQRS server";
56 | String productId = findProductIdByName(productName);
57 |
58 | // @formatter:off
59 | getMockMvc().perform(get("/products/{productId}/backlogItems", productId)
60 | .accept(MediaType.APPLICATION_JSON))
61 | //.andDo(print())
62 | .andExpect(status().isOk())
63 | .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
64 | .andExpect(jsonPath("$[0].backlogItemStory").value("Write documentation"))
65 | .andExpect(jsonPath("$[0].backlogItemPosition").value(0))
66 | .andExpect(jsonPath("$[1].backlogItemStory").value("Add more unit tests"))
67 | .andExpect(jsonPath("$[1].backlogItemPosition").value(1));
68 | // @formatter:on
69 | }
70 |
71 | private String findProductIdByName(String name) {
72 | return (String) JsonPath.read(productsJson, "$content[?].productId[0]", filter(where("productName").is(name)));
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/test/resources/sample.sql:
--------------------------------------------------------------------------------
1 | SET DB_CLOSE_DELAY -1;
2 | ;
3 | CREATE USER IF NOT EXISTS SA SALT '156da56d219be548' HASH '502c79fad489d809b3a5fb71dd92c92ae803ec359bfdadc3dc74983fca227ba7' ADMIN;
4 | CREATE SEQUENCE PUBLIC.SYSTEM_SEQUENCE_309235C0_29B5_45D9_8DF8_EFA82BBE245F START WITH 1 BELONGS_TO_TABLE;
5 | CREATE SEQUENCE PUBLIC.SYSTEM_SEQUENCE_2D04285B_C0F2_45FD_AB2F_84F596E49B76 START WITH 1 BELONGS_TO_TABLE;
6 | CREATE SEQUENCE PUBLIC.SYSTEM_SEQUENCE_99DBA090_0E8D_48A3_B0FB_B8FB0EB1E64F START WITH 5 BELONGS_TO_TABLE;
7 | CREATE SEQUENCE PUBLIC.SYSTEM_SEQUENCE_2C7660DF_4BF3_439C_89E6_8447074213ED START WITH 1 BELONGS_TO_TABLE;
8 | CREATE MEMORY TABLE PUBLIC.T_BACKLOG_ITEM(
9 | C_BACKLOG_ITEM_ID VARCHAR(255) NOT NULL,
10 | C_VERSION INTEGER,
11 | C_PRODUCT_ID VARCHAR(255) NOT NULL,
12 | C_ID VARCHAR(255),
13 | C_STORY VARCHAR(255) NOT NULL
14 | );
15 | ALTER TABLE PUBLIC.T_BACKLOG_ITEM ADD CONSTRAINT PUBLIC.CONSTRAINT_7 PRIMARY KEY(C_BACKLOG_ITEM_ID);
16 | -- 4 +/- SELECT COUNT(*) FROM PUBLIC.T_BACKLOG_ITEM;
17 | INSERT INTO PUBLIC.T_BACKLOG_ITEM(C_BACKLOG_ITEM_ID, C_VERSION, C_PRODUCT_ID, C_ID, C_STORY) VALUES('adfefe13-67ec-4bd1-a5b6-680ae0360feb', 0, 'd1a06697-0914-48b5-969c-52967f7c6f92', NULL, 'Write documentation');
18 | INSERT INTO PUBLIC.T_BACKLOG_ITEM(C_BACKLOG_ITEM_ID, C_VERSION, C_PRODUCT_ID, C_ID, C_STORY) VALUES('0cdc4b57-5624-4778-a1b6-c65a7fd01c0e', 0, 'd1a06697-0914-48b5-969c-52967f7c6f92', NULL, 'Add more unit tests');
19 | INSERT INTO PUBLIC.T_BACKLOG_ITEM(C_BACKLOG_ITEM_ID, C_VERSION, C_PRODUCT_ID, C_ID, C_STORY) VALUES('d81d9671-7a43-4cbe-8c10-30706574c7df', 0, '7c5276e2-afe6-4068-8be8-826eee89cf7e', NULL, 'Apply Twitter Bootstrap');
20 | INSERT INTO PUBLIC.T_BACKLOG_ITEM(C_BACKLOG_ITEM_ID, C_VERSION, C_PRODUCT_ID, C_ID, C_STORY) VALUES('1c49cd4b-4e56-409b-a679-df48113862fd', 0, '7c5276e2-afe6-4068-8be8-826eee89cf7e', NULL, 'Create Angular controllers');
21 | CREATE MEMORY TABLE PUBLIC.T_COMMITED_BACKLOG_ITEM(
22 | C_ENTITY_ID BIGINT DEFAULT (NEXT VALUE FOR PUBLIC.SYSTEM_SEQUENCE_2C7660DF_4BF3_439C_89E6_8447074213ED) NOT NULL NULL_TO_DEFAULT SEQUENCE PUBLIC.SYSTEM_SEQUENCE_2C7660DF_4BF3_439C_89E6_8447074213ED,
23 | C_BACKLOG_ITEM_ID VARCHAR(255) NOT NULL,
24 | C_SPRINT_ID VARCHAR(255) NOT NULL
25 | );
26 | ALTER TABLE PUBLIC.T_COMMITED_BACKLOG_ITEM ADD CONSTRAINT PUBLIC.CONSTRAINT_9 PRIMARY KEY(C_ENTITY_ID);
27 | -- 0 +/- SELECT COUNT(*) FROM PUBLIC.T_COMMITED_BACKLOG_ITEM;
28 | CREATE MEMORY TABLE PUBLIC.T_PRODUCT(
29 | C_PRODUCT_ID VARCHAR(255) NOT NULL,
30 | C_VERSION INTEGER,
31 | C_NAME VARCHAR(255) NOT NULL
32 | );
33 | ALTER TABLE PUBLIC.T_PRODUCT ADD CONSTRAINT PUBLIC.CONSTRAINT_4 PRIMARY KEY(C_PRODUCT_ID);
34 | -- 3 +/- SELECT COUNT(*) FROM PUBLIC.T_PRODUCT;
35 | INSERT INTO PUBLIC.T_PRODUCT(C_PRODUCT_ID, C_VERSION, C_NAME) VALUES('d1a06697-0914-48b5-969c-52967f7c6f92', 4, 'Example DDD/CQRS server');
36 | INSERT INTO PUBLIC.T_PRODUCT(C_PRODUCT_ID, C_VERSION, C_NAME) VALUES('7c5276e2-afe6-4068-8be8-826eee89cf7e', 4, 'Example DDD/CQRS client');
37 | INSERT INTO PUBLIC.T_PRODUCT(C_PRODUCT_ID, C_VERSION, C_NAME) VALUES('6bd24930-e051-47d8-87d0-0df9391cfaa7', 0, 'Product with no backlog items');
38 | CREATE MEMORY TABLE PUBLIC.T_PRODUCT_BACKLOG_ITEM(
39 | C_ENTITY_ID BIGINT DEFAULT (NEXT VALUE FOR PUBLIC.SYSTEM_SEQUENCE_99DBA090_0E8D_48A3_B0FB_B8FB0EB1E64F) NOT NULL NULL_TO_DEFAULT SEQUENCE PUBLIC.SYSTEM_SEQUENCE_99DBA090_0E8D_48A3_B0FB_B8FB0EB1E64F,
40 | C_BACKLOG_ITEM_ID VARCHAR(255) NOT NULL,
41 | C_POSITION INTEGER NOT NULL,
42 | C_PRODUCT_ID VARCHAR(255) NOT NULL
43 | );
44 | ALTER TABLE PUBLIC.T_PRODUCT_BACKLOG_ITEM ADD CONSTRAINT PUBLIC.CONSTRAINT_4B PRIMARY KEY(C_ENTITY_ID);
45 | -- 4 +/- SELECT COUNT(*) FROM PUBLIC.T_PRODUCT_BACKLOG_ITEM;
46 | INSERT INTO PUBLIC.T_PRODUCT_BACKLOG_ITEM(C_ENTITY_ID, C_BACKLOG_ITEM_ID, C_POSITION, C_PRODUCT_ID) VALUES(1, 'adfefe13-67ec-4bd1-a5b6-680ae0360feb', 0, 'd1a06697-0914-48b5-969c-52967f7c6f92');
47 | INSERT INTO PUBLIC.T_PRODUCT_BACKLOG_ITEM(C_ENTITY_ID, C_BACKLOG_ITEM_ID, C_POSITION, C_PRODUCT_ID) VALUES(2, '0cdc4b57-5624-4778-a1b6-c65a7fd01c0e', 1, 'd1a06697-0914-48b5-969c-52967f7c6f92');
48 | INSERT INTO PUBLIC.T_PRODUCT_BACKLOG_ITEM(C_ENTITY_ID, C_BACKLOG_ITEM_ID, C_POSITION, C_PRODUCT_ID) VALUES(3, 'd81d9671-7a43-4cbe-8c10-30706574c7df', 0, '7c5276e2-afe6-4068-8be8-826eee89cf7e');
49 | INSERT INTO PUBLIC.T_PRODUCT_BACKLOG_ITEM(C_ENTITY_ID, C_BACKLOG_ITEM_ID, C_POSITION, C_PRODUCT_ID) VALUES(4, '1c49cd4b-4e56-409b-a679-df48113862fd', 1, '7c5276e2-afe6-4068-8be8-826eee89cf7e');
50 | CREATE MEMORY TABLE PUBLIC.T_RELEASE(
51 | C_RELEASE_ID VARCHAR(255) NOT NULL,
52 | C_VERSION INTEGER,
53 | C_NAME VARCHAR(255) NOT NULL,
54 | C_PRODUCT_ID VARCHAR(255) NOT NULL,
55 | C_RELEASE_DATE TIMESTAMP NOT NULL
56 | );
57 | ALTER TABLE PUBLIC.T_RELEASE ADD CONSTRAINT PUBLIC.CONSTRAINT_9E PRIMARY KEY(C_RELEASE_ID);
58 | -- 2 +/- SELECT COUNT(*) FROM PUBLIC.T_RELEASE;
59 | INSERT INTO PUBLIC.T_RELEASE(C_RELEASE_ID, C_VERSION, C_NAME, C_PRODUCT_ID, C_RELEASE_DATE) VALUES('c1c1bf0e-b397-4dfb-9d1d-3e7a5f207219', 0, 'Release 1', 'd1a06697-0914-48b5-969c-52967f7c6f92', TIMESTAMP '2011-01-14 00:00:00.0');
60 | INSERT INTO PUBLIC.T_RELEASE(C_RELEASE_ID, C_VERSION, C_NAME, C_PRODUCT_ID, C_RELEASE_DATE) VALUES('f85dcccf-2eb0-4ba5-9615-ff45701089bb', 0, 'Release 2', 'd1a06697-0914-48b5-969c-52967f7c6f92', TIMESTAMP '2011-01-29 00:00:00.0');
61 | CREATE MEMORY TABLE PUBLIC.T_REMAINING_AMENDMENT(
62 | C_ENTITY_ID BIGINT DEFAULT (NEXT VALUE FOR PUBLIC.SYSTEM_SEQUENCE_309235C0_29B5_45D9_8DF8_EFA82BBE245F) NOT NULL NULL_TO_DEFAULT SEQUENCE PUBLIC.SYSTEM_SEQUENCE_309235C0_29B5_45D9_8DF8_EFA82BBE245F,
63 | C_EFFECTIVE_DATE TIMESTAMP NOT NULL,
64 | C_HOURS_REMAINING INTEGER NOT NULL,
65 | C_TASK_ID VARCHAR(255) NOT NULL,
66 | C_REMAINING_AMENDMENTS_ORDER INTEGER
67 | );
68 | ALTER TABLE PUBLIC.T_REMAINING_AMENDMENT ADD CONSTRAINT PUBLIC.CONSTRAINT_E PRIMARY KEY(C_ENTITY_ID);
69 | -- 0 +/- SELECT COUNT(*) FROM PUBLIC.T_REMAINING_AMENDMENT;
70 | CREATE MEMORY TABLE PUBLIC.T_SCHEDULED_BACKLOG_ITEM(
71 | C_ENTITY_ID BIGINT DEFAULT (NEXT VALUE FOR PUBLIC.SYSTEM_SEQUENCE_2D04285B_C0F2_45FD_AB2F_84F596E49B76) NOT NULL NULL_TO_DEFAULT SEQUENCE PUBLIC.SYSTEM_SEQUENCE_2D04285B_C0F2_45FD_AB2F_84F596E49B76,
72 | C_BACKLOG_ITEM_ID VARCHAR(255) NOT NULL,
73 | C_RELEASE_ID VARCHAR(255) NOT NULL
74 | );
75 | ALTER TABLE PUBLIC.T_SCHEDULED_BACKLOG_ITEM ADD CONSTRAINT PUBLIC.CONSTRAINT_3 PRIMARY KEY(C_ENTITY_ID);
76 | -- 0 +/- SELECT COUNT(*) FROM PUBLIC.T_SCHEDULED_BACKLOG_ITEM;
77 | CREATE MEMORY TABLE PUBLIC.T_SPRINT(
78 | C_SPRINT_ID VARCHAR(255) NOT NULL,
79 | C_VERSION INTEGER,
80 | C_BEGIN_DATE TIMESTAMP NOT NULL,
81 | C_END_DATE TIMESTAMP NOT NULL,
82 | C_NAME VARCHAR(255) NOT NULL,
83 | C_PRODUCT_ID VARCHAR(255) NOT NULL
84 | );
85 | ALTER TABLE PUBLIC.T_SPRINT ADD CONSTRAINT PUBLIC.CONSTRAINT_49 PRIMARY KEY(C_SPRINT_ID);
86 | -- 2 +/- SELECT COUNT(*) FROM PUBLIC.T_SPRINT;
87 | INSERT INTO PUBLIC.T_SPRINT(C_SPRINT_ID, C_VERSION, C_BEGIN_DATE, C_END_DATE, C_NAME, C_PRODUCT_ID) VALUES('d4345cdc-12d3-4c94-a86b-dba2b0878d43', 0, TIMESTAMP '2011-01-01 00:00:00.0', TIMESTAMP '2011-01-14 00:00:00.0', 'Sprint 1', 'd1a06697-0914-48b5-969c-52967f7c6f92');
88 | INSERT INTO PUBLIC.T_SPRINT(C_SPRINT_ID, C_VERSION, C_BEGIN_DATE, C_END_DATE, C_NAME, C_PRODUCT_ID) VALUES('1ba00f5c-3c21-4c35-92ae-24de16f79f87', 0, TIMESTAMP '2011-01-15 00:00:00.0', TIMESTAMP '2011-01-29 00:00:00.0', 'Sprint 2', 'd1a06697-0914-48b5-969c-52967f7c6f92');
89 | CREATE MEMORY TABLE PUBLIC.T_TASK(
90 | C_TASK_ID VARCHAR(255) NOT NULL,
91 | C_VERSION INTEGER,
92 | C_BACKLOG_ITEM_ID VARCHAR(255) NOT NULL,
93 | C_HOURS_REMAINING INTEGER NOT NULL,
94 | C_NAME VARCHAR(255) NOT NULL,
95 | C_STATUS VARCHAR(255)
96 | );
97 | ALTER TABLE PUBLIC.T_TASK ADD CONSTRAINT PUBLIC.CONSTRAINT_94 PRIMARY KEY(C_TASK_ID);
98 | -- 0 +/- SELECT COUNT(*) FROM PUBLIC.T_TASK;
99 | ALTER TABLE PUBLIC.T_PRODUCT ADD CONSTRAINT PUBLIC.UK_517KAENCTU69XIS4BIAT5V1IE UNIQUE(C_NAME);
100 | ALTER TABLE PUBLIC.T_REMAINING_AMENDMENT ADD CONSTRAINT PUBLIC.FK_4AQNANQB934G8924UEF9R2W38 FOREIGN KEY(C_TASK_ID) REFERENCES PUBLIC.T_TASK(C_TASK_ID) NOCHECK;
101 | ALTER TABLE PUBLIC.T_SCHEDULED_BACKLOG_ITEM ADD CONSTRAINT PUBLIC.FK_L651SROAE12LC4T3NUUL146PO FOREIGN KEY(C_RELEASE_ID) REFERENCES PUBLIC.T_RELEASE(C_RELEASE_ID) NOCHECK;
102 | ALTER TABLE PUBLIC.T_COMMITED_BACKLOG_ITEM ADD CONSTRAINT PUBLIC.FK_PP7D9JORD0YVLO9Q2OKJB7F3F FOREIGN KEY(C_SPRINT_ID) REFERENCES PUBLIC.T_SPRINT(C_SPRINT_ID) NOCHECK;
103 | ALTER TABLE PUBLIC.T_PRODUCT_BACKLOG_ITEM ADD CONSTRAINT PUBLIC.FK_AMF2TH7I5ENG88VUBPJ2DBIKM FOREIGN KEY(C_PRODUCT_ID) REFERENCES PUBLIC.T_PRODUCT(C_PRODUCT_ID) NOCHECK;
104 |
--------------------------------------------------------------------------------
/src/test/resources/test.properties:
--------------------------------------------------------------------------------
1 | jpa.generateDdl=true
2 | jpa.showSql=false
--------------------------------------------------------------------------------