IDDD in JavaScript - Part 1: Running the Original
Implementing Domain-Driven Design – In JavaScript! #
What? #
In this blog series, I will be porting the Identity Access domain sample from Vaughn Vernon’s amazing book Implementing Domain-Driven Design to a node.js application.
Why am I doing this? Cultural arrogance! Java is evil and JavaScript is the best!
OK, no not really. I cut my teeth on the late 20th century wave of programming languages – my first real programming was in Perl, and I played with Perl and Python for almost all of the work where I had a choice, only working with C++ (the language of my CS program) with reluctance. However, my career started and stayed in Java land, and specifically, the Java EE world of big containers, enterprise service buses, business process management software packages, and so on.
Of course, writing enterprise web apps always involved lots of JavaScript, and when I look back at writing a SPA in 2009 … things were different then! I honestly always preferred JavaScript, but it wasn’t “serious”, and for a variety of reasons I dug even further into the enterprise Java world.
In 2015, I suddenly came back across the new world of node.js and was floored at the possibilities! 1 However, digging into this new ecosystem was challenging at first. We don’t often talk about it, but our industry has tribes, and these tribes have different languages and shibboleths that we use to distinguish our affiliation.
Porting this sample provided a journey through which I could personally explore the intersection of these two tribes. What types of design decisions do you need to make a port like this? How do you resolve the difference in expectations between the platforms and ecosystems? If you take the paradigms of one tribe and implement them directly in the other, have you really moved at all? What does “idiomatic” JavaScript mean, and can you write idiomatic JavaScript and still have domain-driven design?
Of course, in order to do this, I first had to get the Java stuff running on my computer, to see if it works. The rest of this post describes the hoops I jumped through to do this. I put in all the mistakes and frustrations along the way, which will be the style of this article series in general.
Running the Samples on a MacBook Air in 2016 #
Step one is easy – get the code on our computer! Who needs a tarball in 2016.
$ git clone https://github.com/VaughnVernon/IDDD_Samples.git
Build it!
$ ./gradlew build
No Java runtime present, requesting install.
OK. Well, let’s install Java.
$ brew cask install java
...
$ java -version
java version "1.8.0_102"
Java(TM) SE Runtime Environment (build 1.8.0_102-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.102-b14, mixed mode)
Let’s try gradle again.
$ ./gradlew build
...
com.saasovation.agilepm.application.product.ProductApplicationServiceTest > testNewProduct FAILED
com.saasovation.common.port.adapter.messaging.MessageException
Caused by: java.net.ConnectException
com.saasovation.agilepm.application.product.ProductApplicationServiceTest > testDiscussionProcess FAILED
com.saasovation.common.port.adapter.messaging.MessageException
Caused by: java.net.ConnectException
...
OK, so, the containers aren’t running, so now we start the containers (in another terminal window)
$ ./startContainers.sh
!! This script requires [docker] to be installed
Let’s install docker! I love Homebrew, in case you can’t tell, so “brew install” is my go to command to resolve missing dependencies.
$ brew install docker
$ ./startContainers.sh start
Starting MySQL Server container...
Cannot connect to the Docker daemon. Is the docker daemon running on this host?
docker: Cannot connect to the Docker daemon. Is the docker daemon running on this host?.
See 'docker run --help'.
Waiting for MySQL Server to be up and running...
^C
Oh, ok… The magic of Google tells me I need to do the following:
https://penandpants.com/2014/03/09/docker-via-homebrew/
$ brew cask install virtualbox
...
? virtualbox was successfully installed!
$ brew install boot2docker
...
? /usr/local/Cellar/boot2docker/1.8.0_1: 3 files, 8M
$ boot2docker init
WARNING: The 'boot2docker' command line interface (not to be confused with
'boot2docker' the operating system) is officially deprecated.
Please switch to Docker Machine (<https://docs.docker.com/machine/>) ASAP.
Docker Toolbox (<https://docker.com/toolbox>) is the recommended install method.
Ugh. Outdating Google documentation is the worst. I follow the links and see the relevant instructions.
$ brew uninstall boot2docker
Uninstalling /usr/local/Cellar/boot2docker/1.8.0_1... (3 files, 8M)
$ brew install docker-machine
? /usr/local/Cellar/docker-machine/0.7.0: 6 files, 39.0M
$ docker-machine create --driver virtualbox default
Creating CA: /Users/dfaulkne/.docker/machine/certs/ca.pem
Creating client certificate: /Users/dfaulkne/.docker/machine/certs/cert.pem
Running pre-create checks...
(default) Image cache directory does not exist, creating it at /Users/dfaulkne/.docker/machine/cache...
(default) No default Boot2Docker ISO found locally, downloading the latest release...
Error with pre-create check: "Error getting a version tag from the Github API response.\nYou may be getting rate limited by Github.
Ugh. Back to google again: https://gist.github.com/christopheranderton/8644743
$ atom ~/.bash_profile
...
$ source ~/.bash_profile
$ echo $HOMEBREW_GITHUB_API_TOKEN # To make sure source worked!
deadbeefdeadbeefdeadbeefdeadbeef
$ docker-machine --github-api-token $HOMEBREW_GITHUB_API_TOKEN create --driver virtualbox default
...
Docker is up and running!
$ eval $(docker-machine env default)
Yay!
$ ./startContainers.sh start
...!! mysql command not found
Sigh. And the utilities package won’t install on El Capitan. I’m impatient at this point and decide to waste bandwidth.
$ brew install mysql
And then start it.
$ ./startContainers.sh start
Starting MySQL Server container...
iddd-mysql
cc45773ff8e76b35a2d3587dd9d4a860cd3aeb5bdb1c3b6b56d25773d91eca3e
Waiting for MySQL Server to be up and running...
Importing [/Users/dfaulkne/ddd/IDDD_Samples/iddd_common/src/main/mysql/test_common.sql]
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 2003 (HY000): Can't connect to MySQL server on '0.0.0.0' (61)
Importing [/Users/dfaulkne/ddd/IDDD_Samples/iddd_collaboration/src/main/mysql/collaboration.sql]
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 2003 (HY000): Can't connect to MySQL server on '0.0.0.0' (61)
Importing [/Users/dfaulkne/ddd/IDDD_Samples/iddd_common/src/main/mysql/common.sql]
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 2003 (HY000): Can't connect to MySQL server on '0.0.0.0' (61)
Importing [/Users/dfaulkne/ddd/IDDD_Samples/iddd_identityaccess/src/main/mysql/iam.sql]
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 2003 (HY000): Can't connect to MySQL server on '0.0.0.0' (61)
Starting RabbitMQ container...
iddd-rabbitmq
65ae2b0330ba4f7990cdb1233277511dacc674b3f847ae11f2d1ff78befb2f12
Waiting for RabbitMQ to be up and running...
And then it hangs forever. :-( The shell script executes a bunch of setup SQL scripts, but it never seemed to work. After a lot of banging my head against the wall, I basically gave up on getting it to run the shell script. I just set the if test to false and ran an import script manually . This is obviously not great, but it works for me.
#if which mysql > /dev/null; then
if 0; then
And then I had to change the hostname, since my hostname was getting rejected by RabbitMQ:
#rabbitmqNodeName=$(hostname)
rabbitmqNodeName="dwfrabbitmq"
So now it’s all running! And … all the tests fail. Never fear, there’s still hope. Maybe I just need to add the IP address? So I do a quick grep of localhost:
$ grep -R localhost .
... # 8 billion results
So, localhost is hardcoded everywhere? Probably easier to port forward. I download this shell script from github:
https://github.com/johanhaleby/docker-machine-port-forwarder/blob/master/pf
and then run “./pf.sh 8080”. And I can get to the RabbitMQ port! Woohoo!
So I re-run the Gradle tests, and then I get a bunch of failed tests, all with the cause:
Caused by: java.lang.IllegalStateException: AnnotationAwareAspectJAutoProxyCreator is only available on Java 1.5 and higher
Googling around takes me to this page
Which says that the old Spring file doesn’t support version 8. So, I don’t care about version 8! Let’s go back to version 7.
$ brew cask uninstall java
$ brew tap caskroom/versions
$ brew cask install java7
...
? java7 was successfully installed!
$ ./gradlew test
And all the tests pass! It’s a miracle!
Part 2, I’ll actually talk about JavaScript.
-
Yes, I know, I missed out. ↩