When working with large projects with a lot of modules one can easily lose track of all the external dependencies. There could be overlapping between transitive dependencies that required libraries bring into the classpath. This is where Maven comes in. 

Coming from Ant there was a lot of confusion for me at first. Ant gives you free hands and a lot of rope. Working with Maven can feel restricted. You have to do things the Maven way. But this convention over configuration approach is great once you get used to it. It enables you to quickly set up your project and it gives you all the usual build phases. For example, including a library in your POM file includes all the transitive dependencies of the included library. There is no need to hunt down all dependencies of a badly packaged library and no need for guessing which of those dependencies are required.

Dependency management

In this example JUnit and all its dependencies are included into classpath.

<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1.0</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
</dependency>
</dependencies>
</project>

When including a dependency we have to define its version. But what about the transitive dependences? And what about when the transitive dependencies of two or more included libraries clash in versions? Dependency management section of POM file gives us control over this. Declaring an artifact in dependency management section controls the version of transitive dependency.

<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1.0</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>test</groupId>
<artifactId>B</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>

Also all child POMs can include dependency from its parent POM dependency management section omitting the version. This way we can control version of a specific library on a scope of a whole project.

Project organization

Large project with a lot of modules is where Maven really shines. All starts from a parent module that contains all other modules. You can organize sub-modules using flat or tree structure. I find that tree structure offers easier navigation between sub-modules. Parent module defines all common dependencies (e.g. JUnit library) and versions of artifacts that sub-modules will use. It can also define build lifecycle (e.g. Java version). That way we can edit those things on a project scope.

Parent POM:

<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>parent-example</artifactId>
<version>1.0</version>
<modules>
<module>module-A</module>
<module>module-B</module>
</modules>
</project>

Child POM:

<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.mycompany.app</groupId>
<artifactId>parent-example</artifactId>
<version>1.0</version>
</parent>
<groupId>com.mycompany.app</groupId>
<artifactId>module-A</artifactId>
<version>1.0</version>
</project>

Child POM can have its own modules and that way we build a tree structure. If applicable we can define a project version in top parent POM and use it in all sub-modules. That way we need to change it in only one place when needed.

0 comments