chapter eleven

11 Refining dependencies and APIs

 

This chapter covers:

  • handling dependencies that are part of a module’s API
  • aggregating and refactoring modules without breaking clients
  • defining optional dependencies
  • writing code in the face of absent dependencies
  • exporting packages to selected modules only

Chapter 3 explains how requires and exports directives are the basis for readability and accessibility. But these mechanisms are quite strict: Every module has to be explicitly required, all required modules have to be present for the application to compile and launch, and exported packages are accessible to all other modules. This suffices for the majority of use cases but there is still a significant portion in which these solutions are too broad.

The most obvious use case is optional dependencies, which a module wants to compile against but which are not necessarily present at run time. Spring, for example, does this with the Jackson databind library. If you run a Spring application and want to use JSON as a data transfer format, you can get support for that by simply dropping in the Jackson artifact. If, on the other hand, that artifact is absent, Spring is happy as well—it just doesn’t support JSON then. So while Spring uses Jackson it does not require it.

11.1  Implied readability—passing dependencies on

11.1.1  Exposing a module’s dependencies

11.1.2  The transitive modifier—implying readability on a dependency

11.1.3  When to use implied readability

11.1.4  When to rely on implied readability

11.1.5  Refactoring modules with implied readability

11.2  Optional dependencies

11.2.1  The conundrum of reliable configuration

11.2.2  The static modifier—marking dependencies as optional

11.2.3  Module resolution of optional dependencies

11.3.1  Exposing internal APIs