Dodging Dependency Nightmares with a Serialization Module
In Serializing POJOs with Jackson we looked at creating simple classes just for serialization. In this article, we pull those classes into their own module, DRY and ready to be reused. This is a little more work up front, but it's more of an investment than a cost.
This is a popular starting module structure on my projects:
1+ locomotives2|3|--- webservice4|--- acceptance-tests
Our new wrinkle from the previous post is we now have some classes that our web services want to return and our acceptance-tests want to create.
Many folks immediately see only two options:
- duplicate all of the serialization classes
- introduce a dependency between acceptance-tests and webservice modules
It's easy to have a knee-jerk reaction to violating the DRY principal by duplicating classes and instead add the rest of the application as a dependency.
This must be avoided. Acceptance tests should be black-box tests, knowing nothing of the implementation. Allowing the acceptance tests to access the rest of the codebase easily spirals into a tangled mess. Suddenly our tests can taken any view of the world they like - including accessing datastores directly and skipping validations. The door is now wide open for testing low-value states of the system that aren't even possible for users to create. Even worse, the tests can become so coupled to internal code that it becomes difficult to refactor and improve the application. All of these consequences work against the original intent of our tests - to be a safety net.
Adding a new module is the best option. We're going to take our nice, new,
dependency-free serialization classes and move them into their own module. The
dropwizard folks recommend calling this
api, and I think that's a great
1+ locomotives2|3|--- webservice4|--- api5|--- acceptance-tests
Now we have a third module and our dependency tree looks much better.
Putting this kind of thought into a growing application is an important part of continuing to evolve a codebase efficiently, leaving little chance of introducing unwanted coupling to accidental dependencies.