Generell gibt es zwei Deployment-Varianten.
- Frontend Standalone
- Frontent integriert in Backend
Bei der ersten Variante wird die Web-Anwendung Standalone deployt. Die statischen Web-Dateien (HTML, CSS, JavaScript, Bilder usw.) werden über einen Webserver direkt oder z.B. in einem Docker Image ausgeliefert. Eigenschaften dieser Variante:
- Eigenständige, unabhängige vom Backend, Releasezyklen möglich
- Frontend-Projekt kann separat deployt werden
Bei der zweiten Variante wird das Frontend-Projekt in ein Backend-Projekt integriert. Das Ziel dabei ist es, dass im Deployment-Artifakt des Backends die statischen Dateien des Frontends (Output vom /dist
Ordner) integriert und danach über eine bestimmte URL auf dem Backend ausgeliefert werden. Eigenschaften dieser Variante:
- Gleiche Frontend und Backend Releasezyklen
- Gleiche Frontend und Backend Versionen
- Beide Projekte werden zusammen deployt, z.B. als WAR mit der Distribution des Frontends im Web-Resource-Ordner.
Die erste Deployment-Variante kann mit dem Exec Maven Plugin gebaut werden. Dabei werden zwei Befehle hintereinander ausgeführt: npm install
und npm run <script>
. An der Stelle von <script>
steht je nach Vorhaben ein npm
Script-Befehl für einen Development- oder Release-Build. Für das Demo Projekt aus diesem GitHub-Repository würde der Script-Befehl entweder build:dev
oder build:prod
heißen. Die Wahl eines Script-Befehls kann über ein Maven-Profil gesteuert werden. Das Profil für die tägliche Entwicklung muss per Default aktiviert werden. Die relevante Konfiguration in der pom.xml
Datei sieht wie folgt aus:
<?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">
...
<artifactId>some-frontend-project</artifactId>
<packaging>pom</packaging>
...
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.5.0</version>
<executions>
<execution>
<id>install-npm</id>
<phase>generate-resources</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>npm</executable>
<arguments>
<argument>install</argument>
</arguments>
</configuration>
</execution>
<execution>
<id>exec-build</id>
<phase>prepare-package</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>npm</executable>
<arguments>
<argument>run</argument>
<argument>${env.build}</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<!-- No java in this module -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>default-compile</id>
<phase>none</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<!-- Profile for development build (activated per default) -->
<profile>
<id>development</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<env.build>build:dev</env.build>
</properties>
</profile>
<!-- Profile for production build (Maven release plugin uses this profile automatically) -->
<profile>
<id>release</id>
<activation>
<property>
<name>performRelease</name>
<value>true</value>
</property>
</activation>
<properties>
<env.build>build:prod</env.build>
</properties>
</profile>
</profiles>
</project>
Bei der zweiten Deployment-Variante muss die Konfiguration sowohl im Frontend- als auch im Backend-Projekt vorgenommen werden. Das Frontend-Projekt wird genauso wie bei der ersten Variante mit dem Exec Maven Plugin gebaut. Der Unterschied besteht darin, dass jetzt ein Maven-Artefakt erzeugt werden muss, damit es dem Buildprozess des Backend-Projektes zur Verfügung steht. D.h. der Build muss paketiert werden. Das Maven Assembly Plugin hilft uns dabei diese Aufgabe zu meistern. Angenommen, der Frontend-Build erstellt eine Distribution im Verzeichnis target/dist
unter dem Projekt-Wurzelverzeichnis. Das Maven Assembly Plugin baut aus dem target/dist
Ordner ein tar.gz
Archiv. Die oben gezeigte Konfiguration muss um den folgenden Abschnitt erweitert werden:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>create-tarball</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>assembly.xml</descriptor>
</descriptors>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
</execution>
</executions>
</plugin>
Das Paketieren ist in der Datei assembly.xml
definiert.
<assembly>
<id>assembly</id>
<formats>
<format>tar.gz</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>${project.build.directory}/dist</directory>
<outputDirectory>/</outputDirectory>
</fileSet>
</fileSets>
</assembly>
Das Backend-Projekt muss zuerst das tar.gz
Archiv entpacken. Das geschieht in der pom.xml
Datei des Backend-Projektes mit Hilfe von Maven Dependency Plugin. Die entsprechende Konfiguration sieht folgendermaßen aus:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<executions>
<execution>
<id>unpack</id>
<phase>generate-sources</phase>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<includeTypes>tar.gz</includeTypes>
<includeArtifactIds>some-frontend-project</includeArtifactIds>
<outputDirectory>${project.build.directory}/frontend-dist</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
Angenommen, die Web-Applikation wird als WAR-Datei gebaut und deployet. Die entpackte Frontend-Distribution kann zuerst mit Hilfe von Maven Resources Plugin nach beispielsweise src/main/webapp
kopiert werden. Danach erzeugt das Maven WAR Plugin eine WAR-Datei. Vereinfacht sieht die relevante POM-Konfiguration des Backend-Projektes ungefähr so aus (um die Idee zu verdeutlichen):
<?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">
...
<artifactId>some-backend-project</artifactId>
<packaging>war</packaging>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<executions>
<execution>
<id>unpack</id>
<phase>generate-sources</phase>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<includeTypes>tar.gz</includeTypes>
<includeArtifactIds>some-frontend-project</includeArtifactIds>
<outputDirectory>${project.build.directory}/frontend-dist</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<id>copy-resources-src</id>
<phase>process-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.basedir}/src/main/webapp</outputDirectory>
<resources>
<resource>
<directory>${project.build.directory}/frontend-dist</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
</plugin>
</plugins>
</build>
</project>