Git submodules

Apiumhub - Jun 27 '22 - - Dev Community

General overview

This article is based on Git Submodules (Git tools) and real implementation in a production-grade application with several µ-monoliths calling each other via REST APIS. So, in this article, we’ll see a simplified example of a Spring RestTemplate modularization in the Api Client submodule.

Git Submodules allows you to modularize your application components into modules with some pros and cons.

PROS

  • Versability to modify main and submodules code in the same (IDE) project and commit/push to each remote repository separately.
  • Enforces the Single Responsibility Principle decoupling specialized code into submodules from parent projects. So, removes duplications.
  • No need to create and publish a Nexus artifact jar file.

CONS

  • As a submodule/library for several main projects, compatibility it’s a must. There are some workarounds that can be applied like branching incompatible versions and/or using several pom-parentproject.xml in the submodule as explained later.

In addition, a git merge-request pipeline in the submodule should run all parent git repository pipelines to cover possible compilation and integration errors.

Pre-requisites

  • Git
  • IDE (Optional)
  • Maven 3.6
  • JDK11

Initial setup

Git repositories

Examples created for this article:

How to do for new projects from scratch

Steps to set up NEW projects with a common git submodule:

  • Create Git repositories for all projects and submodules
  • Clone mss1 and mss2 repositories and add submodule in each:
git submodule init
git submodule add -b main https://github.com/davidgfolchApium/gitsubmodule-apiclient
Enter fullscreen mode Exit fullscreen mode

Clone example

Once we have the git repositories created and linked with the submodule, we can just clone the parent repository this way:

mkdir git-submodules && cd git-submodules/
git clone --recurse-submodules https://github.com/davidgfolchApium/gitsubmodule-mss1
git clone --recurse-submodules https://github.com/davidgfolchApium/gitsubmodule-mss2
Enter fullscreen mode Exit fullscreen mode

GGkAXwr HFpKajbfLY3JYPE8 lrSBjY6ptHYlgmvddSzK9Wq 030dP0a2rWybWcmiAUgrWOup BhHbo85b1yEida jyw aJvW kbQ7PquxmJr7

Maven dependencies

I’m going to explain the “child pom per parent module” strategy to solve the problem when parent projects have different library versions (Spring-Boot versions f.ex.).

As you can see in the parent mss1 pom.xml, it’s using spring-boot-starter-parent 2.6.7 and in the modules section it’s pointing to api-client api-client/pom-mss1.xml and the api implementation module (that is a maven module but not a git submodule):

<parent>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-parent</artifactId>
   <version>2.6.7</version>
   <relativePath/> <!-- lookup parent from repository -->
</parent>

<groupId>com.apiumhub.articles.git-submodules</groupId>
<artifactId>mss1-parent</artifactId>
<version>1.1.0-SNAPSHOT</version>
<packaging>pom</packaging>

<name>mss1 parent</name>
<description>Parent mss1 git project using api-client git submodule</description>

<modules>
   <module>api</module>
   <module>gitsubmodule-apiclient/pom-mss1.xml</module>
</modules>

<properties>
   <java.version>1.11</java.version>
   <maven-compiler-plugin-java.version>11</maven-compiler-plugin-java.version>
   <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
   <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
   <spring-boot.version>2.2.2.RELEASE</spring-boot.version>
   <start-class>com.apiumhub.articles.gitsubmodules.Application</start-class>
   <lombok.version>1.18.16</lombok.version>
   <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
   <maven-source-plugin.version>3.2.1</maven-source-plugin.version>
</properties>
Enter fullscreen mode Exit fullscreen mode

In addition, defines common dependencies and versions.

In the api maven module we have the parent reference and the api-client dependency

<parent>
   <groupId>com.apiumhub.articles.git-submodules</groupId>
   <artifactId>mss1-parent</artifactId>
   <version>1.1.0-SNAPSHOT</version>
</parent>

<artifactId>api</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>

<dependencies>
   <dependency>
       <groupId>com.apiumhub.articles.git-submodules</groupId>
       <artifactId>api-client-mss1</artifactId>
       <version>1.0.0-SNAPSHOT</version>
       <scope>compile</scope>
   </dependency>
</dependencies>
Enter fullscreen mode Exit fullscreen mode

And the submodule api-client/pom-mss1.xml says that mss1 pom.xml is it’s parent pom. So, it heritages all from both parents: Spring Boot maven parent module -> git-sumodules-mss1, and there is no need to put versions:

<parent>
   <groupId>com.apiumhub.articles.git-submodules</groupId>
   <artifactId>mss1-parent</artifactId>
   <version>1.1.0-SNAPSHOT</version>
</parent>

<artifactId>api-client-mss1</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
Enter fullscreen mode Exit fullscreen mode

Decoupled org.springframework.web.client package

Checkout that the dependency of org.springframework.web.client package is only used (and should be only used) in the submodule. This completely decouples the implementations on this package in the base projects and it could be changed easily, for example, with a new WebFlux implementation of ApiClient.

tozifGiBEg2Xox4PgZM99IjveOHxqo4QZg8I ktvtRDedIWSqpMVG0JUoQCv 6YLS1bGTYkAhWKHv7fBeAwRvEkkGuzmV6 FTCGN3xFDuOp5nKb e0hb0d0rUi5 vdnvq2FFOF vejyBIZRCBQ

In some cases like integration or feature tests, where you need to mock RestClient , should be also implemented in the api-client submodule test/src/.. folder to keep source code decoupled from main the mssX.

Working with submodules

We will have a project like that imported on intellij with the mmsX , api and api-client maven modules:

kZaWYEM Z TZWmoZxXOGWBYi0DjEy6lPAY4saueqQ9Zeivzak14pI4gPM3w

Just to show another example, committing changes in Intellij will look like this:

wlYQ1o1W8yNHNVWk6PKQ1dqUAgqMQ7K8YTc6TAISi6MDjTRentAeh5comibL4twW Xba5FB le8J cZetmTHtk4eH6F7J fupteB8eDrRj4AObAFmYIF3AN jubz0WEOIt TZBOMC9JiHZNfw

We can see the parent maven module & the child modules. In this case, all are using the main branch from git, so Intellij doesn’t show any branch information, otherwise, you will see branch info in place.

In the push operations, we just see the 2 git repos for this mss:

aK 0VTIOtATuhCGfu gXenD 0IbfRFIL Aa9z5V82VguRaIBQEieTRP EstBtl2vp048hg9bKatwC1io6oHURqFl8VeueGMmIUNPgX H5DsLnv1Ip3ClsHkBeRKO4a9V3HkfzOdl7HRjSlWA

Running the application

Run both mss1 and mss2 as any Spring Boot application. In the main folder of each mss compile and run:

mvn clean install
cd api
mvn spring-boot:run
Enter fullscreen mode Exit fullscreen mode

NOTE: application.properties -> server.port not working in base parent modules, because the RestTemplate @Bean is defined in the child maven submodule and Spring is trying to get the properties files from it’s classpath, so a ServerPortCustomizer.java is needed to set ports.

In the mss1 and mss2, we have the same endpoints:

http://localhost:8081/user/info

sw7ZURrU01jo2GhKBNMA7qTdFIP 8z CIr9X8tDR1JWx3bYB7lrql 7OXdSf0dlhn9SBDP 9kR2ed4Q5Exw fRQmeNCtee AODN tb4Q V4rAew

http://localhost:8081/bank/info

http://localhost:8081/api/client/exception

http://localhost:8082/user/info

GnFwlY2hiD3DA0mUSKMmOb mBZxIgizSwucLcPWardb0mf9eSG040v3aK5tSjrWRkMYfwbkQmI7UdN7e0 PUcGqNZElEF4WPJDE9am9KC3hlvonqD1Q4I9lb9 PWihD 90XWX68RPOO7 tDeEA

http://localhost:8082/bank/info

hI2OVhDUUEVVyOeEzfHlWjd3 hrI0U2YN1h4f77 1p1uE68UStc9UN8gYBEWFb O4RGjcgWZvxrAdSMBC0oniLlzKRpUlkIh2vueLACbp 8CfLcYEk8y2cTB2Ar uUQdB hCTcFb IPgubDyeg

http://localhost:8082/api/client/exception

As you can check on the implementation mss1 (in port 8081) is calling to mss2 (8082) via ApiClient on the /bank/info endpoint, and mss2/user/info is calling to mss1/user/info.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player