Dejo aquí este codekata o howto de copia y pega para montar la estructura de un proyecto general con Maven, usando Spring Boot en el backend y Angular en el frontend. La idea de este post es automatizar el despliegue en producción de este tipo de proyectos, cómo comenzar a montar el despliegue continuo.
En líneas generales, consite en compilar el micro-servicio Spring Boot embebiendo todo el frontend en Angular dentro del mismo empaquetado .jar. Con unas configuraciones en los ficheros POM de Maven, y en el JSON de configuración Angular, podemos automatizarlo.
Vamos al grano..
Escenario del proyecto
Con estos proyectos podemos tener por ejemplo una estructura de 3 ficheros poms:
- pom.xml
- backend/pom.xml
- frontend/pom.xml
Desde el pom de la raiz entonces podremos lanzar:
mvn clean install
..y de forma automática limpiará todos los compilados Java, compilará el frontend al directorio de recursos del backend, y finalmente compilará el backend con la versión en curso de todo junto.
Finalmente nos quedará un fichero:
backend/target/jnj-backend-1.0.0.jar
..que podremos ejecutar en el servidor simplemente lanzando lo siguiente, o añadiendo más parámetros para configurarlo:
java -jar jnj-backend-1.0.0.jar
Fichero POM de Maven principal
Este fichero simplemente es un POM de Maven multiproyecto normal y corriente, parecido al de un post anterior sobre multi-proyectos:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.jnjsite</groupId>
<artifactId>jnj-main</artifactId>
<version>1.0.0</version>
<name>Jnj Main</name>
<packaging>pom</packaging>
<modules>
<module>frontend</module>
<module>backend</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/>
</parent>
</project>
Aquí podemos establecer el orden con el que se compilará según el orden de los tag <module>.
El fichero POM de Maven para Angular con el frontend
Lo siguiente para automatizar es la instalación de Node junto con NPM para el frontend. Esto podemos hacerlo con el plugin frontend-maven-plugin de maven:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.jnjsite</groupId>
<artifactId>jnj-main</artifactId>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>com.jnjsite</groupId>
<artifactId>jnj-frontend</artifactId>
<version>1.0.0</version>
<name>Jnj Frontend</name>
<description>Project made with Angular</description>
<packaging>pom</packaging>
<build>
<plugins>
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<version>1.12.1</version>
<configuration>
<workingDirectory>./</workingDirectory>
<nodeVersion>v16.15.1</nodeVersion>
<npmVersion>8.14.0</npmVersion>
</configuration>
<executions>
<execution>
<id>install node and npm</id>
<goals>
<goal>install-node-and-npm</goal>
</goals>
</execution>
<execution>
<id>npm install</id>
<goals>
<goal>npm</goal>
</goals>
</execution>
<execution>
<id>npm run build</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>run build</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Ahora, lo siguiente más sencillo que he visto es cambiar el directorio de compilación del frontend de Angular, al directorio público del backend de Spring Boot. Lo podemos hacer cambiando el fichero frontend/angular.json para que apunte al directorio del backend que corresponde:
"outputPath": "../backend/src/main/resources/public",
Otra alternativa es dejarlo como está, pero entonces haría falta un plugin de Maven para recoger los compilados de Angular desde el backend.
El fichero POM de Maven para Spring Boot con el backend
Ahora queda entonces compilar el backend y a correr. Sólo hay que definir el parent para que compile desde el principal:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.jnjsite</groupId>
<artifactId>jnj-main</artifactId>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>com.jnjsite</groupId>
<artifactId>jnj-backend</artifactId>
<version>1.0.0</version>
<name>Jnj Backend</name>
<description>Project made with Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.9</version>
</dependency>
</dependencies>
<build>
<!-- <directory>../target/</directory> -->
<plugins>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>1.5.8</version>
<executions>
<execution>
<id>generate-docs</id>
<phase>prepare-package</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>html</backend>
<doctype>book</doctype>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-asciidoctor</artifactId>
<version>${spring-restdocs.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Terminando y referencias
Para terminar queda probar desde el directorio principal, y desde cada subproyecto si tenemos que comprobar por separado. Desde el principal se debería de ver algo como lo siguiente empezando así:
..y si todo ha ido bien terminando así:
..y ya es suma y sigue para lanzarlo, o seguir automatizando el despliegue, sobre el nuevo fichero compilado:
java -jar jnj-backend-1.0.0.jar
Sólo me queda remitirte a la documentación oficial:
https://maven.apache.org/
https://spring.io/quickstart
https://angular.io/docs