Parent pom and microservices

Here there is one issue with dependency and dependency management. Say one of your micro service wants to upgrade to newer version of common for some reason...you cant do that as you have parent. I do understand temptation of reducing duplication of redundant things like plugin configuration. In micro service we need to think more about independence of each service.

Some config like say your repository or release configuration etc can be common.


The 'problem' with a multi-module parent pom is that, without complicated profiles, it locks the modules in the same release cycle (assuming you're using the Release Plugin, which you should be).

The way I work with Maven is to have a parent pom that declares:

  • common dependencies (logging APIs, JUnit, etc).
  • common plugins.
  • all dependencies in the dependencyManagement section.
  • all plugins in the pluginManagement section.

Each module delcares the parent pom as its parent but the parent knows nothing about the modules.

The benefit of this comes from the last to two bullets above, the 'management' sections. Anything contained in a 'management' section needs to be redeclared in a module that wants to use a particular dependency or plugin.

For example the parent might look like this:

<project>

  <groupId>com.example</groupId>
  <artifactId>parent</artifactId>
  <version>1.0.00-SNAPSHOT</version>

  ...

  <dependencies>

    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.7</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>            

  </dependencies>

  <dependencyManagement>

    <dependency>
      <groupId>commons-lang</groupId>
      <artifactId>commons-lang</artifactId>
      <version>2.6</version>
    </dependency>        

    <dependency>
      <groupId>commons-collections</groupId>
      <artifactId>commons-collections</artifactId>
      <version>2.1</version>
    </dependency>

  </dependencyManagement>

  <plugins>

    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>3.1</version>
      <configuration>
        <source>1.8</source>
        <target>1.8</target>
      </configuration>
    </plugin>

  <plugins>

  <pluginManagement>

    <plugins>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-assembly-plugin</artifactId>
        <version>2.4</version>
        <configuration>
          <appendAssemblyId>false</appendAssemblyId>
          <descriptors>
            <descriptor>src/main/assembly/assembly.xml</descriptor>
          </descriptors>
        </configuration>
        <executions>
          <execution>
            <id>make-assembly</id>
            <phase>package</phase>
            <goals>
              <goal>single</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

    </plugins>

  </pluginManagement>

</project>

And the module might look like this:

<project>

  <parent>
    <groupId>com.example</groupId>
    <artifactId>parent</artifactId>
    <version>1.0.00-SNAPSHOT</version>
  </parent>

  <groupId>com.example</groupId>
  <artifactId>module</artifactId>
  <version>1.0.00-SNAPSHOT</version>

  <dependencies>

    <dependency>
      <groupId>commons-lang</groupId>
      <artifactId>commons-lang</artifactId>          
    </dependency>        

  </dependencies>

  <plugins>

    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-assembly-plugin</artifactId>
    </plugin>

  </plugins>

</project>

The module would:

  • have dependencies on org.slf4j:slf4j-api:1.7.7:compile, junit:junit:4.11:test and commons-lang:commons-lang:2.6:compile.
  • has the plugin org.apache.maven.plugins:maven-assembly-plugin:2.4

I would definitely use a parent project.

I've been working for years with both the structures...Microservices and not, modular or not, Ant, Maven and Gradle..

We need to understand that using a parent pom does not mean talk about microservices not coupled and independent:

  • they can be still independent and not coupled using parent pom,
  • they can be still built release and updated in isolation even if you are using a parent pom.

I heard saying "a microservice may need to use different versions for a dependency", well you can, just override the dependency in the specific microservice pom.

We need to focus on "What are here the benefit and what are the cons":

  • Control and standardization: I can manage the common dependencies (with the dependencies management) in a single point, it makes easier to roll out dependencies changes across all the modules, yes we may need different third parties version, but same time we need to avoid losing control over all the dependencies, so exceptions may be allowed but they needs to be balanced with the "standardization"
  • Group management: I can still release just a single module, but I can also manage multi modules releases in a easier way, without having to release module by module, but simply the modules that are under development, in this case I still have a single entry point and all the common dependencies can be overviews withing the parent

And much more:

  • common third parties and platform dependencies management
  • common third parties and platform standardization
  • Full control of the dependencies ecosystem withing the whole application (structured in micro services)
  • common plugins management and standardization
  • reduce duplication and redundant logic.
  • Configurations management and standardization
  • Easier maintenance, change in one place instead of potentially 30 places!!
  • easier to test and roll out common change.

What about the cons? I don't see any for the moment, as exceptions can be managed through overriding common behaviour in the specific microservices pom, I can still manage anything in isolation (build in isolation, release in isolation, deploy in isolation..) There is nothing coupled

Not sure yet what we mean with "it locks the modules in the same release cycle" It does not, unless you are using external SNAPSHOT, I can release a microservice in isolation re-using the same parent version.

for example I can have module 1 declaring Parent 1.0 and be released in isolation without having to run the release process from the parent, I can run it directly on the submodule, but I need to not declare any external SNAPSHOT within the submodule project (you would have same issues with or without parent)


I would avoid dependencies in the parent pom. It's awkward if one of your (independent) microservices would want some other things. It's weird to have the parent know of each microservice.

You can stick with dependencyManagement though to suggest default versions/scopes if you want. A parent pom is, non the less, very convenient to define plugins, repositories and the like.

Instead, I would group a common set of dependencies into a specific artifact(s), that may be only a single pom with dependencies. Then you can depend on, say "com.example/common-db-dependencies/1.2" to include a standard set of database dependencies, like hibernate, apache derby and JPA specs. (or whatever you're using). A service does not use JPA/SQL could avoid that dependency all together.

Don't entangle yourself though. It's easy to overwork dependency structures if you're trying to cover each case. So, only try to standardize things that really get used by a majority of the services.