Recently I stumbled upon an example of why managing your dependencies in a maintainable way is important. The issue I had was in a multi-module project where individual Maven dependencies were overridden everywhere, the result was that a lot of dependencies were in conflict and upgrading these dependencies became quite troublesome. One of the countermeasures for me was the use of certain Maven Bill of Material (BOM).

With using a BOM I only had to set version of dependencies once and did not have to duplicate the dependency in the parent because the bom implicitly has it already.

For example: a lot of projects depend on org.fasterxml.jackson and one of its dependencies (for example: jackson-core, jackson-dataformat-csv, jackson-databind) which can be bothersome to keep an overview of them all. This will make maintainance harder and also increases the risk of creating conflicts in dependencies.

Using the BOM

For my multi-module maven project I added com.fasterxml.jackson.jackson-bom to the dependencyManagement in the parent POM.

Important is to realize that a BOM is just another POM file that declares different dependencies so add <type>pom</type> to the dependency to let maven know. In addition adding <scope>import</scope> is necessary to ensure the bom dependencies are imported in your project.

In the parent POM I added the BOM in the <dependencyManagement/> section:

<dependencyManagement>
    <dependencies>
<!-- A single dependency that provides needed and compatible dependencies-->
         <dependency>
             <groupId>com.fasterxml.jackson</groupId>
             <artifactId>jackson-bom</artifactId>
             <version>${jackson.version}</version>
             <type>pom</type>
             <scope>import</scope>
         </dependency>
    </dependencies>
</dependencyManagement>

In the child POM I added the specific dependencies I needed for that module:

<dependencies>
<!--    actual adding the dependencies only where it is used-->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.dataformat</groupId>
        <artifactId>jackson-dataformat-csv</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
</dependencies>

As you see I only maintain one version for the single BOM in my project which will ensure I have access to all its dependencies on demand.

<dependencies/> vs <dependencyManagement/>

One of the other ways to help with managing dependencies centralized in Maven projects is the use of the <dependencyManagement/> in the root/parent pom. Using dependencyManagement instead of only dependencies give you a way to define and declare said dependencies but not actually add them.

Adding the dependencies to the project is strictly done via <dependencies/> in the specific POM file. This way you can define the dependency and the version in 1 place and only actually add it where the dependency is used.

Conclusion

Using Maven Bill Of Material is an easy way to declare them once and use them only where needed.

With using a BOM you will:

  • Reduce risk of dependency conflicts: Using a BOM you have a verified set of dependencies that are compatible with each other.

  • More secure: updating the BOM itself updates all its dependencies also.

  • Better readability and maintainability: All dependencies are managed by a single BOM so reading it will be easier and there are less duplicate dependencies.

shadow-left