This is a guest post by Robert Hensing.
In this blog post, I describe my experience adapting a microservice for use with AWS' DynamoDB.
The Sock Shop is Weave Works' reference microservice implementation. For their customers, they want to demonstrate that their cloud solutions for deployment, monitoring and more work well on Amazon Web Services.
Therefore the task is to adapt one of the services, the orders service, to use Amazon's DynamoDB instead of MongoDB.
The original orders service was created with Spring Boot. Spring Boot is built on the Spring framework. Whereas Spring provides you with a Swiss Army knife for calling your setters for you, AOP wrappers, et cetera, the Spring Boot library focuses on ‘configuring’ your application automatically.
That's easy when you're setting up a new application or microservice. How I wish it made things simple.
My task was to introduce a new persistence back end, which I started out with happily, knowing that using dependency injection makes this possible. The service was already using Spring after all.
As you must have guessed from the tone and the title, things did not go so well. I ran into a series of problems, most of which could be worked around.
Spring Boot's autoconfiguration, which is triggered by the presence of classes on the classpath, had to be configured to ignore some ‘autoconfigurations’ depending on the runtime configuration. That turned out ugly, with a ‘missing bean factory exception’. Apparently the bean factory, basically a big bag of objects like database connection pools and such, was not quite there yet.
Docker has some potential for making projects build a bit more reliably. That did not quite work out for this project though. A build script was in place that compiles the project inside a temporary docker container, but it reused the host user's Maven repository (package cache), which is bad for accurate reproducibility, and, worse, it writes root-owned files there, locking out all tools that run on the host. No-one noticed that before, since I was apparently the first one to work from a Linux host. I have fixed it for this project.
After a couple of days of trial and error, I hit upon a big problem. Apparently the Spring Data component for DynamoDB, which is just a community module, does not have special support that is required for Spring HATEOAS. This, combined with the ugliness that I had to introduce into the configuration, and the risk that the annotations on the domain classes will be inappropriate for one of the persistence back ends, made me decide to propose a rewrite of the service.
For the rewrite, I made the following decisions:
There is a wide range of HTTP server implementations for the JVM. I have considered the following:
As it turns out, Jersey, the library for providing the API comes with its own dependency injector. That was one of those scary moments, but luckily you can also construct the object graph of endpoints and mappers and such by hand.
For testing I have re-used the Dredd-based test suite of the original service. Sadly I did not have the time to look into the full details of the framework. It seems to be like doctest, running the examples from a piece of documentation, but that did not seem to be quite the way it was used in the original orders service, which relied a lot on hooks.
I have decided to skip unit testing for this service, because the bulk of the code is about integration and there is basically no risk to be covered in this demo app.
I have tried to use the swagger specification to generate API scaffolding or to derive an interface for the code to implement, but the swagger code generator failed on the spec when generating Java code, with a NullPointerException, probably because that is what Jackson returns when something is missing. I would have fixed the API spec, but not if that means debugging maven first. The Scala code generation did succeed, but was not very usable with my Java-esque approach. I was able to reuse the generated Scala classes for scaffolding, which was nice.
At this point the API is not entirely compatible with the old version, but it is sufficient for the front end.
Support for asynchronicity and parallellism would be nice to have for performance. Async programming is pretty nice in Scala, but the integration with the Java libraries is probably not as clean. It is a bit sad that the JVM needs asynchronous programming for performance.
Looking back at the effort, I can conclude
Besides the technical stuff, I would like to conclude it was nice to work for Container Solutions. I got in contact with them through their meetups, got talking and discovered that I could do some interesting stuff for them alongside the other things I do. It is quite a young company with a positive, balanced atmosphere.