Table of Contents
Visión general
Este artículo está basado en submódulos Git (Git tools) y en una implementación real en una aplicación de producción con varios µ-monolitos que se llaman entre sí a través de APIS REST. Así, en este artículo, veremos un ejemplo simplificado de modularización de Spring RestTemplate en el submódulo Api Client.
Git Submodules te permite modularizar los componentes de tu aplicación en módulos con algunos pros y contras.
PROS
- Posibilidad de modificar el código principal y el de los submódulos en el mismo proyecto (IDE) y enviarlos a cada repositorio remoto por separado.
- Aplica el principio de responsabilidad única desacoplando el código especializado en submódulos de los proyectos padre. Por lo tanto, elimina las duplicaciones.
- No es necesario crear y publicar un archivo jar de artefactos de Nexus.
CONTRAS
- Como submódulo/biblioteca para varios proyectos principales, la compatibilidad es una necesidad. Hay algunas soluciones que se pueden aplicar como la ramificación de versiones incompatibles y/o el uso de varios pom-parentproject.xml en el submódulo como se explica más adelante.
Además, un pipeline git merge-request en el submódulo debería ejecutar todos los pipelines del repositorio git padre para cubrir posibles errores de compilación e integración.
Requisitos previos
- Git
- IDE (Opcional)
- Maven 3.6
- JDK11
Setup inicial
Repositorios Git
Ejemplos creados para este artículo:
Cómo hacer para nuevos proyectos desde cero
Pasos para configurar NUEVOS proyectos con un submódulo git común:
- Crear repositorios Git para todos los proyectos y submódulos
- Clona los repositorios mss1 y mss2 y añade un submódulo en cada uno:
git submodule init
git submodule add -b main https://github.com/davidgfolchApium/gitsubmodule-apiclient
Ejemplo Clone
Una vez que tenemos los repositorios git creados y enlazados con el submódulo, podemos simplemente clonar el repositorio padre de esta manera:
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
Dependencias Maven
Voy a explicar la estrategia de «pom hijo por módulo padre» para resolver el problema cuando los proyectos padres tienen diferentes versiones de librerías (versiones de Spring-Boot, por ejemplo).
Como puedes ver en el pom.xml del parent mss1, está usando spring-boot-starter-parent 2.6.7 y en la sección de módulos está apuntando a api-client api-client/pom-mss1.xml y al módulo de implementación de la api (que es un módulo de maven pero no un submódulo de git):
<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>
Además, define las dependencias y versiones comunes.
En el módulo maven api tenemos la referencia padre y la dependencia api-client
<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>
Y el submódulo api-client/pom-mss1.xml dice que mss1 pom.xml es su parent pom. Por lo tanto, hereda todo de ambos padres: Módulo padre de Spring Boot maven -> git-sumodules-mss1, y no es necesario poner versiones:
<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>
Paquete org.springframework.web.client desacoplado
Comprueba que la dependencia del paquete org.springframework.web.client sólo se utiliza (y debería utilizarse) en el submódulo. Esto desacopla completamente las implementaciones de este paquete en los proyectos base y podría cambiarse fácilmente, por ejemplo, con una nueva implementación de WebFlux de ApiClient.
En algunos casos, como las pruebas de integración o de características, en las que se necesita imitar a RestClient, también se debe implementar en la carpeta api-client submodule test/src/.. para mantener el código fuente desacoplado del mssX principal.
Trabajar con submódulos
Tendremos un proyecto así importado en intellij con los módulos de maven mmsX, api y api-client:
Sólo para mostrar otro ejemplo, la confirmación de los cambios en Intellij se verá así:
Podemos ver el módulo maven padre y los módulos hijos. En este caso, todos están utilizando la rama principal de git, por lo que Intellij no muestra ninguna información de la rama, de lo contrario, verá información de la rama en su lugar.
En las operaciones push, sólo vemos los 2 repos git para este mss:
Ejecución de la aplicación
Ejecuta tanto mss1 como mss2 como cualquier aplicación de Spring Boot. En la carpeta principal de cada mss compilar y ejecutar:
mvn clean install
cd api
mvn spring-boot:run
NOTA: application.properties -> server.port no funciona en los módulos base padre, porque el RestTemplate @Bean está definido en el submódulo hijo de maven y Spring está tratando de obtener los archivos de propiedades de su classpath, por lo que se necesita un ServerPortCustomizer.java para establecer los puertos.
En el mss1 y mss2, tenemos los mismos puntos finales:
http://localhost:8081/user/info
http://localhost:8081/bank/info
http://localhost:8081/api/client/exception
http://localhost:8082/user/info
http://localhost:8082/bank/info
http://localhost:8082/api/client/exception
Como puedes comprobar en la implementación mss1 (en el puerto 8081) está llamando a mss2 (8082) a través de ApiClient en el endpoint /bank/info, y mss2/user/info está llamando a mss1/user/info.
Author
-
15 years as a technical/functional analyst and java backend engineer. 8 years: backend/full-stack SOLID architecture designs and POC's implementations… leading & mentoring dev teams in best practices
Ver todas las entradas