├── .github └── workflows │ └── gradle.yml ├── .gitignore ├── ACKNOWLEDGEMENTS.md ├── CODE_OF_CONDUCT.md ├── README.md ├── build.gradle ├── config └── checkstyle │ └── checkstyle.xml ├── docs └── Domain-Model.puml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src ├── main └── java │ └── com │ └── harmellaw │ ├── CriminalCase.java │ ├── CriminalOffence.java │ ├── PNCId.java │ ├── PoliceInvestigation.java │ ├── PreChargeDecision.java │ └── Suspect.java └── test └── java └── com └── harmellaw ├── ACriminalCase.java ├── APoliceInvestigation.java └── APreChargeDecision.java /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Gradle 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle 3 | 4 | name: Build 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up JDK 1.8 20 | uses: actions/setup-java@v1 21 | with: 22 | java-version: 1.8 23 | - name: Grant execute permission for gradlew 24 | run: chmod +x gradlew 25 | - name: Build with Gradle 26 | run: ./gradlew build 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /build/ 3 | 4 | # Ignore Gradle GUI config 5 | gradle-app.setting 6 | 7 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 8 | !gradle-wrapper.jar 9 | 10 | # Cache of project 11 | .gradletasknamecache 12 | 13 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 14 | # gradle/wrapper/gradle-wrapper.properties 15 | 16 | # IDE config files 17 | /.idea/ 18 | -------------------------------------------------------------------------------- /ACKNOWLEDGEMENTS.md: -------------------------------------------------------------------------------- 1 | I couldn't have done this without help from many other people. 2 | 3 | Thankyou to every one of you. 4 | 5 | * My wife and kids for everything you do for me every single day 6 | * My ThoughtWorker colleagues who replied to my survey about how folks reason about and design domains these days 7 | * Rich Halford for your overpowering enthusiasm at the very outset 8 | * @bapostma for the C# version of the solutions 9 | * Bruck Eckell for your gift of the "beginners mind" 10 | * Barry Hawkins and Chris Phelps for your confidence in my ability to do this 11 | * Gayathri Thiyagarajan for teaching me more about DDD than anyone else 12 | * Mauro Vilasi and Jon Barber for your feedback on early ideas 13 | * Vanessa Formicola for yoru later feedback 14 | * Izzy Harris, Amalie Smidth, Tas Torkia, Jade Forsberg and Shazia Kasuji for your detailed and honest feedback on the beta version of the course 15 | * Melissa Duffield for your support in getting this course to 16 | * Lindsay Ventimiglia for showing me the ropes of the O'Reilly training platform, and for keeping me calm on the first official run 17 | * @jimtyhurst for fixing the out of date Gradle build file (and @goodmike and @bruce-koho for raising the problem in the first place) 18 | * Everyone who left feedback - thankyou! 19 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | Our Code of Conduct 2 | =================== 3 | 4 | The First Steps in DDD community is all about learning, discussion, 5 | inclusion and connection, and we want everyone to feel as comfortable as 6 | possible. Although we cannot guarantee you will have a good time, we can 7 | certainly do our best to make sure you don't have a *bad* time, and the 8 | baseline for this is that no one should ever be harrassed. 9 | 10 | To achieve this goal, all participants 11 | are required to agree with this code of conduct (CoC). Organizers will enforce 12 | this CoC at all times. We expect cooperation from all participants to 13 | help ensure a safe environment for everybody. 14 | 15 | This CoC applies for all aspects of this community (not just during 16 | formal training sessions). 17 | 18 | We do not tolerate harassment of participants in any form. Harassment includes 19 | offensive verbal comments related to disability, gender, gender identity and 20 | expression, age, sexual orientation, physical appearance, body size, race, 21 | ethnicity, religion, technology choices, sexual images in public spaces, 22 | deliberate intimidation, stalking, following, harassing photography or 23 | recording, sustained disruption of talks or other events, inappropriate 24 | physical contact, and unwelcome attention of any kind. 25 | 26 | Participants violating these rules are expected to immediately cease the 27 | offending behavior upon notification. Participants who continue to violate 28 | these rules after being notified may be sanctioned or expelled at the 29 | discretion of the community organisers. 30 | 31 | Points of Contact 32 | ----------------- 33 | 34 | We recognize that managing harassment is not the job 35 | of its recipients. The community organisaer and delegated individuals (*contact 36 | persons*) will make themselves available to address any & all complaints of 37 | harassment, and the relevant contact information for them will be 38 | made available to upon request to 39 | [@andrewharmellaw](https://github.com/andrewharmellaw), the originator of this 40 | community. 41 | 42 | What You Can Expect 43 | ------------------- 44 | 45 | If there is a problem and you reach out to one of the contact persons, you 46 | can expect that: 47 | 48 | 1. You will be heard and trusted. 49 | 50 | 2. Other organizers (if any) will be informed. 51 | 52 | 3. A discussion will be initiated between the contact person and/or 53 | organizers and the person in question, to stop the behavior in question 54 | and possibly include a warning to that person. 55 | 56 | If you're not sure that behavior counts as harrassment, please reach out to any 57 | contact person. We are happy to talk it over with you. 58 | 59 | Our Goal for this CoC 60 | --------------------- 61 | 62 | There are usually laws and rules and social norms about these issues. But 63 | those don't seem to be enough, as we've seen times. There are 64 | unspoken practices that are considered acceptable within subgroups, but never 65 | written down, and these produce behaviors that are considered acceptable as a 66 | result. Because no one says anything or puts a stake in the ground about these 67 | behaviors, it seems OK to those practicing them. Saying nothing doesn't work. 68 | 69 | We don't, however, want people to be terrified that they might accidentally 70 | say something wrong, and if they do make such a faux pas they will immediately 71 | be forcibly ejected. Simply punishing mistakes without giving someone the 72 | opportunity to learn is not the goal of this CoC. 73 | 74 | The CoC tells you when you've made a mistake, and empowers others to have a 75 | conversation with you about it. And most importantly, it tells newcomers & 76 | traditionally marginalized attendees that we value their safety. The focus is on 77 | the goal that "you will not be harassed." 78 | 79 | There are times when it doesn't matter what you do, someone might be having a 80 | bad day and feel anger or some other emotion regardless of who is nearby or 81 | what they do. The CoC shouldn't be a weapon in that situation, on either end. 82 | The CoC draws the line on harassment — it says what you cannot do (harass) 83 | but it doesn't say what you must do (speak a certain way). 84 | 85 | If You are Having a Hard Time Communicating 86 | ------------------------------------------- 87 | 88 | These [Principles of Nonviolent Communication](https://www.cnvc.org/Training/NVC-Concepts) 89 | might be helpful: 90 | 91 | 1. Differentiate observation from evaluation. 92 | 93 | - Carefully observe what is happening, free of evaluation. 94 | 95 | - Specify behaviors and conditions that affect you. 96 | 97 | 2. Differentiate feeling from thinking. 98 | 99 | - Identify and express internal feeling states in a way that does not 100 | imply judgment, criticism, blame or punishment. 101 | 102 | 3. Frame your experience within universal human needs that are 103 | being met or not met in relation to what is happening and how you are feeling. 104 | 105 | - Examples: connection, sustenance, trust, and understanding. 106 | 107 | 4. Make a Request. 108 | 109 | - Clearly and specifically state what you *do* want, not what you don’t 110 | want. 111 | 112 | - Make a request and not a demand. If it's truly a request, there is no 113 | consequence for saying "no." 114 | 115 | - Before making the request, check your feelings to ensure you are not 116 | attempting to motivate, however subtly, through fear, guilt, shame, or 117 | obligation. 118 | 119 | - A genuine request allows the other person to give you a gift. 120 | 121 | The Four Agreements, By Don Miguel Ruiz 122 | --------------------------------------- 123 | 124 | 1. **Be Impeccable with your Word:** Speak with integrity. Say only what you mean. Avoid using the Word to speak against yourself or to gossip about others. Use the power of your Word in the direction of truth and love. 125 | 126 | 2. **Don’t Take Anything Personally:** Nothing others do is because of you. What others say and do is a projection of their own reality, their own dream. When you are immune to the opinions and actions of others, you won’t be the victim of needless suffering. 127 | 128 | 3. **Don’t Make Assumptions:** Find the courage to ask questions and to express what you really want. Communicate with others as clearly as you can to avoid misunderstandings, sadness and drama. With just this one agreement, you can completely transform your life. 129 | 130 | 4. **Always Do Your Best:** Your best changes from moment to moment; it is different when you are healthy as opposed to sick. Under any circumstance, simply do your best, and you will avoid self-judgment, self-abuse, and regret. 131 | 132 | Be Kind. Assume the best. 133 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # First Steps in Domain-Driven Design - Java Solution 2 | ![Build](https://github.com/andrewharmellaw/first-steps-in-ddd/workflows/Build/badge.svg) [![Known Vulnerabilities](https://snyk.io/test/github/andrewharmellaw/first-steps-in-ddd/badge.svg?targetFile=build.gradle)](https://snyk.io/test/github/andrewharmellaw/first-steps-in-ddd?targetFile=build.gradle) 3 | 4 | This is the starter project for the exercises in the "First Steps in Domain-Driven Design" course for O'Reilly. If you are more of a dotnet person, take a look at the [C# version of this codebase](https://github.com/First-Steps-in-DDD-Community/first-steps-in-ddd-solutions-dotnet/blob/main/README.md) (Thanks to @BAPostma for contributing this). 5 | 6 | You should be able to fork and then clone this repo to get an almost-empty gradle project ready to work with. 7 | 8 | ## Pre-requisites 9 | * Java JDK (at least Java 8) 10 | * IDE of your choice 11 | * Git client (unless you already have one in your IDE) 12 | * You don't need to have Gradle as the gradle wrapper is part of this project and will download gradle 7 for you. You might however want to use another gradle binary (such as the one which comes with IntelliJ IDEA) which should work fine 13 | 14 | ## What you get 15 | A directory `first-steps-in-ddd` containing this README.md, a gradle build file with JUnit 5 dependencies 16 | declared, standard `src` and `test` directory structure, and some example unit tests 17 | (`APoliceInvestigation.java`, `APreChargeDecision.java` and `ACriminalCase.java`) and associated starter 18 | classes (e.g. `PoliceInvestigation.java`, `PNCId.java`, `Suspect.java`) and an Enum, `CriminalOffence.java`. 19 | 20 | ## Up and running 21 | The training relies entirely on your writing unit tests (ideally you use test-driven development) 22 | so you want to be able to run them very quickly. 23 | 24 | Open the newly checked out project in your IDE (probably point it at the build.gradle, or simply the 25 | top-level project directory). Then check you can execute all the tests with the click of a single mouse 26 | button, or ideally a single keyboard shortcut. 27 | 28 | Additionally, it can be handy to check your build on the command line. Open a terminal and change to the 29 | top-level project directory. Then run the command `./gradlew build`. You ought to see your code and tests 30 | compile, and all but one of the tests run successfully. 31 | 32 | The failing test is where we will start exercise one. 33 | 34 | ## Get ahead of the game 35 | We're going to work on this code based on a [Domain Expert Statement from a Public Prosecutor](https://docs.google.com/document/d/1HpRJj1lk_M80Xvwzs5F-lZ1oACkVNeWRMG0s7BQxZzk/edit?usp=sharing). If you want to read it in advance, that'll help you in the first workshop exercise and beyond. 36 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | } 4 | 5 | group 'com.harmellaw' 6 | version '1.0-SNAPSHOT' 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | implementation 'org.apache.commons:commons-collections4:4.4' 14 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.1' 15 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.1' 16 | } 17 | 18 | test { 19 | useJUnitPlatform() 20 | } 21 | 22 | apply plugin: 'checkstyle' 23 | 24 | checkstyle { 25 | toolVersion = '8.32' 26 | ignoreFailures=false 27 | maxWarnings = 0 28 | } 29 | -------------------------------------------------------------------------------- /config/checkstyle/checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 52 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 69 | 70 | 71 | 73 | 74 | 75 | 81 | 82 | 83 | 84 | 87 | 88 | 89 | 90 | 91 | 94 | 95 | 96 | 97 | 98 | 100 | 101 | 102 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 120 | 122 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 169 | 170 | 171 | 172 | 174 | 175 | 176 | 177 | 179 | 180 | 181 | 182 | 184 | 185 | 186 | 187 | 189 | 190 | 191 | 192 | 194 | 195 | 196 | 197 | 199 | 200 | 201 | 202 | 204 | 205 | 206 | 207 | 209 | 210 | 211 | 212 | 214 | 215 | 216 | 217 | 219 | 221 | 223 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 253 | 254 | 255 | 258 | 259 | 260 | 261 | 266 | 267 | 268 | 269 | 272 | 273 | 274 | 275 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 289 | 290 | 291 | 292 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 308 | 309 | 310 | 311 | 312 | -------------------------------------------------------------------------------- /docs/Domain-Model.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | hide empty members 3 | hide empty methods 4 | 5 | class CriminalCase { 6 | } 7 | 8 | class CriminalOffence { 9 | } 10 | 11 | class PNCId { 12 | } 13 | 14 | class PoliceInvestigation { 15 | } 16 | 17 | class PreChargeDecision { 18 | } 19 | 20 | class Suspect { 21 | } 22 | 23 | Suspect *-- "*" CriminalOffence 24 | 25 | PoliceInvestigation *-- "*" Suspect 26 | 27 | PoliceInvestigation *-- "*" PNCId 28 | 29 | @enduml 30 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/First-Steps-in-DDD-Community/first-steps-in-ddd-solutions/866c9ad85ac580e84482bde7ee852eaad3ff4221/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 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 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /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 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 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 Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | * 4 | * The settings file is used to specify which projects to include in your build. 5 | * 6 | * Detailed information about configuring a multi-project build in Gradle can be found 7 | * in the user guide at https://docs.gradle.org/4.8/userguide/multi_project_builds.html 8 | */ 9 | 10 | rootProject.name = 'first-steps-in-ddd' 11 | -------------------------------------------------------------------------------- /src/main/java/com/harmellaw/CriminalCase.java: -------------------------------------------------------------------------------- 1 | package com.harmellaw; 2 | 3 | public class CriminalCase { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/com/harmellaw/CriminalOffence.java: -------------------------------------------------------------------------------- 1 | package com.harmellaw; 2 | 3 | /* 4 | * List from https://www.cps.gov.uk/sites/default/files/documents/publications/annex_1a_table_of_offences_scheme_c.pdf 5 | */ 6 | public enum CriminalOffence { 7 | 8 | DANGEROUS_DRIVING("Dangerous driving"), 9 | ENDANGERING_AN_AIRCRAFT("Endangering an aircraft"), 10 | FALSE_ACCOUNTING("False accounting"), 11 | IMPERSONATING_CUSTOMS_OFFICER("Impersonating Customs officer"), 12 | KEEPING_A_DISORDERLY_HOUSE("Keeping a disorderly house"), 13 | CORRUPTION_IN_PUBLIC_OFFICE("Corruption in public office"), 14 | CUTTING_AWAY_BUOYS_ETC("Cutting away buoys etc"), 15 | FALSE_EVIDENCE_BEFORE_EUROPEAN_COURT("False evidence before European Court"), 16 | FIRING_ON_REVENUE_VESSEL("Firing on Revenue vessel"), 17 | FRAUDULENT_EVASION_OF_AGRICULTURAL_LEVY("Fraudulent evasion of agricultural levy"), 18 | OBSTRUCTING_ENGINE_OR_CARRIAGE_ON_RAILWAY("Obstructing engine or carriage on railway"); 19 | 20 | private final String value; 21 | 22 | CriminalOffence(String value) { 23 | this.value = value; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/harmellaw/PNCId.java: -------------------------------------------------------------------------------- 1 | package com.harmellaw; 2 | 3 | public class PNCId { 4 | 5 | public final String value; 6 | 7 | public PNCId(String value) { 8 | this.value = value; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/harmellaw/PoliceInvestigation.java: -------------------------------------------------------------------------------- 1 | package com.harmellaw; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | public class PoliceInvestigation { 7 | 8 | public PNCId pncId; 9 | public Set suspects = new HashSet<>(); 10 | 11 | public PoliceInvestigation(PNCId pncId, Suspect suspect) { 12 | if (pncId == null) throw new IllegalArgumentException("You must provide a PNC Id"); 13 | if (suspect == null) throw new IllegalArgumentException("You must provide a suspect"); 14 | 15 | this.pncId = pncId; 16 | this.suspects.add(suspect); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/harmellaw/PreChargeDecision.java: -------------------------------------------------------------------------------- 1 | package com.harmellaw; 2 | 3 | public class PreChargeDecision { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/com/harmellaw/Suspect.java: -------------------------------------------------------------------------------- 1 | package com.harmellaw; 2 | 3 | public class Suspect { 4 | 5 | private final CriminalOffence offence; 6 | 7 | public Suspect(CriminalOffence offence) { 8 | this.offence = offence; 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/com/harmellaw/ACriminalCase.java: -------------------------------------------------------------------------------- 1 | package com.harmellaw; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | 5 | public class ACriminalCase { 6 | 7 | @BeforeEach 8 | public void setup() { 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/com/harmellaw/APoliceInvestigation.java: -------------------------------------------------------------------------------- 1 | package com.harmellaw; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertNotNull; 4 | import static org.junit.jupiter.api.Assertions.assertThrows; 5 | import static org.junit.jupiter.api.Assertions.assertTrue; 6 | 7 | import org.junit.jupiter.api.BeforeEach; 8 | import org.junit.jupiter.api.Test; 9 | 10 | public class APoliceInvestigation { 11 | 12 | PNCId pncId; 13 | Suspect suspect; 14 | PoliceInvestigation anInvestigation; 15 | 16 | @BeforeEach 17 | public void setup() { 18 | pncId = new PNCId("1234-ESDT"); 19 | suspect = new Suspect(CriminalOffence.FALSE_ACCOUNTING); 20 | anInvestigation = new PoliceInvestigation(pncId, suspect); 21 | } 22 | 23 | @Test 24 | public void mustHaveAPoliceNationalComputerId() { 25 | assertNotNull(anInvestigation.pncId); 26 | } 27 | 28 | @Test 29 | public void cannotBeCreatedWithAnEmptyPoliceNationalComputerId() { 30 | Exception exception = assertThrows(IllegalArgumentException.class, () 31 | -> anInvestigation = new PoliceInvestigation(null, suspect)); 32 | assertTrue(exception.getMessage().contains("You must provide a PNC Id")); 33 | } 34 | 35 | @Test 36 | public void cannotBeCreatedWithNoSuspect() { 37 | Exception exception = assertThrows(IllegalArgumentException.class, () 38 | -> anInvestigation = new PoliceInvestigation(pncId, null)); 39 | assertTrue(exception.getMessage().contains("You must provide a suspect")); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/com/harmellaw/APreChargeDecision.java: -------------------------------------------------------------------------------- 1 | package com.harmellaw; 2 | 3 | import static org.junit.jupiter.api.Assertions.fail; 4 | 5 | import org.junit.jupiter.api.BeforeEach; 6 | import org.junit.jupiter.api.Disabled; 7 | import org.junit.jupiter.api.Test; 8 | 9 | public class APreChargeDecision { 10 | 11 | @BeforeEach 12 | public void setup() { 13 | } 14 | 15 | @Disabled("So the CI build stays green. Remove this to get coding.") 16 | @Test 17 | public void shouldRecordAlternativeOffenceAdviceAgainstSuspects() { 18 | fail(); 19 | } 20 | 21 | } 22 | --------------------------------------------------------------------------------