29
  • We have several projects that are microservices, every project is independent (running on separate spring boot server, exposing rest services, using separate DB schema...)
  • We use maven to manage the dependencies.

Is it a good idea to have a parent pom declaring each microservices as modules? And so helping to manage the common dependencies (like the lib servlet-api witch is used in every project, to remove it of all of them and declare it in only the parent pom)

Nick Holt
  • 33,455
  • 4
  • 52
  • 58
Emilien Brigand
  • 9,943
  • 8
  • 32
  • 37
  • Can you please share a link on how to setup a project with Maven and involving microservices. I have a maven-based spring monolithic server which I am planning to migrate from. – technazi Apr 25 '16 at 11:24

6 Answers6

19

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:

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
Nick Holt
  • 33,455
  • 4
  • 52
  • 58
9

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.

Petter Nordlander
  • 22,053
  • 5
  • 50
  • 84
  • 15
    we finally removed completely the parent pom in order to have one git repo by micro service. So now each micro service has his own lifecycle and it's pretty cool :) we don t have a common project to manage the common dependencies, because it can make sense that just one of the micro service need to up the version of a lib (even a lib used in the other micro services) without impacting the others.... – Emilien Brigand Apr 13 '15 at 15:50
6

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)

ivoruJavaBoy
  • 1,307
  • 2
  • 19
  • 39
4

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.

Jigar Shah
  • 2,576
  • 6
  • 31
  • 53
  • 11
    If one service needs to use a specific version, you can redeclare that version in that service, while still keeping everything else common by default. – dan carter Sep 26 '18 at 01:50
3

Most books on microservice architecture recommend autonomy as a principle. Using a parent pom violates that principle.

First of all with a parent pom you can no longer adopt a polyglot approach and write your microservices in different languages.

You'll also be forced to use the dependencies prescribed by the parent, especially if the enforcer plugin is employed.

The microservices will no longer be independently deployable.

There is also the risk that your work on any one microservice may break others if that work involves altering the parent.

Johnny Alpha
  • 758
  • 1
  • 8
  • 35
  • 1. When we using pom, actually we want to use some common technology not different languages. 2. Dependencies can be overwrite in nested modules. 3. Dependency of micro services not related to multi module. We have already a multi module project that each module cat deploy independently. 4. Such risk is common when a shared lib or component should be used. – Erfankam Jul 15 '20 at 10:31
2

A major drawback of using a parent pom approach with microservices is it will make the release management for microservices a slightly tricky affair. Few related pointers -

  • The parent pom should not be frequently changed, should be managed as a separate project in a separate repo.
  • Every change to the parent pom should increment the parent pom version. Once the changes are finalized, the parent pom repo should also be tagged. (treating is as a separate library with independent releases)
  • Moreover the child pom of all the microservices being touched should ideally be updated to point to the latest parent pom version (affecting the autonomy of microservices to some extent). This may also lead to forceful ask of upgrading the microservice to use newer versions of the libraries, which may not always be a feasible option.
  • Even if the only change in a microservice is to point to the new parent pom version, it would call for a new (mostly minor) version release of the service.

Suggestions -

  • You can use the maven enforcer plugin to check for duplicate dependency versions specified between parent and child poms.
  • The parent pom will not be a good option for extensive dependencies and dependency-management, but can certainly be used for things like repositories, distribution management, and plugin management which shall generally not have clashes between microservices.