Spring Boot für technische Anwendungen - Projektsetup und Basiskonfiguration
heat-convection-app: Referenzprojekt für moderne Java-Backend-Entwicklung
Spring Boot Java Projektsetup Applikationsstruktur Softwarearchitektur Backend heat-convection-app
Einleitung
Bei meinem ersten Referenzprojekt, der heat-conduction-app stand die Frage im Mittelpunkt, wie sich ein technisches Ingenieurmodell in eine moderne Softwareanwendung überführen lässt. Der Schwerpunkt lag dabei auf Architekturprinzipien, fachlicher Modellierung und der schrittweisen Entwicklung einer Webanwendung. Dafür habe ich bewusst Python und Flask gewählt. Der leichtgewichtige Stack eignet sich hervorragend, um grundlegende Konzepte sichtbar zu machen un einzelne Architekturschritte zu erklären.
Mit zunehmenden Komplexität steigen jedoch auch der Implementierungs- und Konfigurationsaufwand. Viele Funktionen, die in größeren Anwendungen benötigt werden, müssen zunächst selbst integriert und miteinander verbunden werden. Aus diesem Grund entsteht mit der heat-convection-app ein zweites Referenzprojekt. Obwohl beide Anwendungen fachlich eng miteinander verwandt sind, verfolgen sie unterschiedliche Ziele.
Ziel der heat-convection-app
Bei der heat-convection-app liegt der Fokus auf der praktischen Umsetzung einer professionellen Java- und Spring Boot-Anwendung. Ich möchte zeigen welche Schritte notwendig sind, um aus einer technischen Fachanwendung eine erweiterbare Backend-Anwendung zu entwickeln. Java und Spring Boot bieten hierfür ein umfangreiches Ökosystem aus Frameworks und Bibliotheken. Viele typische Anforderungen produktiver Anwendungen, wie Persistenz, Validierung, Security und Testing, lassen sich dadurch strukturiert umsetzen.
Gerade diese Stärke kann zu Beginn jedoch auch eine Herausforderung sein. Durch die Vielzahl an Möglichkeiten und die umfangreiche Autokonfiguration verliert man leicht den Blick für die tatsächlich notwendigen ersten Schritte. An dieser Stelle möchte ich mit dem Artikel anknüpfen, nicht in dem ich die internen Abläufe von Spring Boot erkläre, sondern vielmehr, wie ich ein Spring Boot Projekt mit dem Fokus auf eine technische Anwendung initialisieren, konfigurieren und aufbauen würde.
Im Mittelpunkt stehen dabei:
- sinnvolle Startabhängigkeiten
- eine saubere Projekt- und Paketstruktur
- erste Basiskonfigurationen
Die eigentliche Implementierung der Fachlogik folgt in den kommenden Artikeln schrittweise.
Die Projektinitialisierung
Bevor mit der Implementierung begonnen werden kann, muss das Projekt erstellt und konfiguriert werden. Dabei verfolge ich meist eine ähnliche Vorgehensweise.
Projektdefinition
Zunächst sollten die fachlichen und technischen Rahmenbedingungen definiert werden. Dazu gehören neben dem fachlichen Modell auch erste Entscheidungen zur Softwarearchitektur und zum Technologiestack.
Fachliches Modell
In der heat-convection-app starte ich mit der Berechnung des konvektiven Wärmeübergangs an einer längs überströmten Platte. Die technische Applikationslogik unterteile ich in 5 funktionale Bausteine:
- Domänenmodell - Welche fachlichen Objekte und Größen existieren?
- Berechnungslogik - Wie werden diese Größen fachlich miteinander verknüpft?
- Eingabeschicht - Wie gelangen konkrete Werte ins System?
- Ausgabeschicht - Wie werden Ergebnisse dargestellt oder weitergeben?
- Orchestrierung - Wie greifen die einzelnen Bausteine kontrolliert ineinander?
Diese fachlichen Bausteine stellen noch keine Softwarearchitektur dar. Sie dienen vielmehr als fachliche Referenzstruktur, um Ingenieurwissen unabhängig von konkreten Technologien oder Frameworks zu strukturieren.
Wer dazu mehr erfahren möchte, findet in meinem Artikel Vom Fourierschen Gesetz zur Softwarelogik eine ausführliche Beschreibung anhand der Wärmeleitung.
Softwarearchitektur
Die grundlegendste Architekturentscheidung ist zunächst die Umsetzung als Webanwendung. Darauf aufbauend folgen weitere Entscheidungen hinsichtlich des Technologie-Stacks, beispielsweise die Wahl der Programmiersprache, des Frameworks, der Persistenzlösung oder des Frontends.
Fragen zu Datenbank, Frontend und Deployment sollten bereits früh mitgedacht werden, auch wenn viele dieser Entscheidungen später noch angepasst oder schrittweise erweitert werden können. Eine mögliche Herangehensweise, die Anwendung in Form von Entwicklungsphasen aufzubauen, habe ich ausführlicher in meinem Artikel Moderne Architektur für technische Web-Applikationen beschrieben.
Beim Frontend ergeben sich grundsätzlich mehrere Optionen:
- API-First / Headless Backend - die Anwendung stellt ausschließlich eine REST-API bereit.
- SSR-Frontend (Server-Side Rendering) - die Benutzeroberfläche ist Bestandteil der Anwendung.
- Externes Frontend - Frontend und Backend sind getrennte Anwendungen und kommunizieren über eine REST-API.
Welche Variante sinnvoll ist, hängt von der jeweiligen Aufgabenstellung ab. Für diesen Beitrag verzichte ich zunächst vollständig auf ein Frontend und konzentriere mich auf die Backend-Struktur.
Das Thema Datenbank werde ich dagegen von Beginn an berücksichtigen. Wer zunächst keinen eigenen Datenbankserver wie PostgreSQL betreiben möchte, kann problemlos mit einer eingebetteten Datenbank wie H2 starten. Eine spätere Migration auf PostgreSQL oder andere produktive Datenbanksysteme ist in der Regel mit überschaubarem Aufwand möglich.
Für dieses Projekt fällt die erste Festlegung daher wie folgt aus:
- Backend: Java und Spring Boot
- Datenbank: Spring Data JPA/Hibernate, Flyway Migrationen und H2
- Testing: Junit5 und Mockito
Diese Entscheidungen bereits zu Beginn zu treffen ist hilfreich, da im nächsten Schritt bei der Erstellung des Projektgerüsts die entsprechenden Abhängigkeiten und Pakete ausgewählt werden.
Projektgerüst & Applikationsstruktur - Spring Initializr
Jede Implementierung startet zunächst mit der Erstellung des Projektgerüsts. Für Spring Boot-Projekte bietet es sich an hier den Spring Initializr zu verwenden. Basierend auf einigen grundlegenden Angaben, wie Sprache, Spring Boot Version, Project Metadata, Java Version und ausgewählten Dependencies wird ein lauffähiges Projekt angelegt, das als Ausgangspunkt für die Implementierung dient.
Bereits an dieser Stelle lohnt es sich, die voraussichtlich benötigten Dependencies mitzudenken. Zwar können weitere Bibliotheken jederzeit ergänzt werden, eine erste sinnvolle Auswahl erleichtert jedoch die spätere Projektstruktur und reduziert nachträgliche Anpassungen.
Für mein Referenzprojekt und die ersten Schritte würde ich folgende Dependencies auswählen:
- Spring Boot DevTools (Developer Tools)
- Spring Security (Security)
- Spring Web (Web)
- Spring Data JPA (SQL)
- Flyway Migration (SQL)
- H2 Database (SQL)
- Validation (I/O)
Der Spring Initializr liefert neben den entsprechenden
spring-boot-starter-*-Dependencies auch die
entsprechenden
spring-boot-starter-*-test Dependencies (ab Spring
Boot 4), so dass die für das Testen notwendigen Bibliotheken
auch vorhanden sind.
CleanUp und Basiskonfiguration
Das erstellte Projekt ist lauffähig und
mvn test läuft erfolgreich durch.
Zunächst würde ich einen kleinen clean-up machen und die
Testklasse
HeatConvectionAppApplicationTests löschen. Diese
Testklasse startet mittels @SpringBootTest den
vollständigen Spring Application Context. Für die ersten
Implementierungsschritte wird ein vollständiger Integrationstest
jedoch meist noch nicht benötigt und kann zunächst entfernt
werden.
pom.xml
Die pom.xml würde ich ebenfalls etwas aufräumen und
zunächst die leeren Tags wie:
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
löschen. Eine Projekt-<description> ,<project.build.sourceEncoding>
und <java.version> würde ich noch ausfüllen
bzw. hinzufügen:
<description>Reference application for technical calculations using Spring Boot</description>
<properties>
...
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>21</java.version>
</properties>
application*.yaml Profile
Teil der Basiskonfiguration ist die Erweiterung der
application*.yaml. Hier würde ich von Anfang an auf
ein übergeordnetes Default application.yml und eine
Development spezifische application-dev.yml setzen.
Zu einem späteren Zeitpunkt kann die Projektkonfiguration noch
um eine application-test.yml und
application-prod.yml ergänzt werden. Ich bevorzuge
hier mehrere application*.yml Dateien, alternativ
kann man auch Multi-Document-YAML-Files anlegen. Hier trennt man
Profile mittels ---:
---
spring:
config:
activate:
on-profile: dev
...
---
Welche Variante verwendet wird, hängt letztlich von Projektgröße und persönlicher Präferenz ab.
spring:
application:
name: "heat-convection-app"
version: "0.0.1-SNAPSHOT"
jpa:
open-in-view: false
show-sql: false
hibernate:
ddl-auto: none
properties:
hibernate:
"[format.sql]": true
flyway:
enabled: true
locations: classpath:db/migration
logging:
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} %-5level [%logger{36}] - %msg%n"
Neben den Anwendungsinformationen gehören dazu
jpa und flyway-Konfiguration und ein
angepasstes Logging-Pattern.
In der application-dev.yml geht es konkret darum
die embedded H2-Datenbank anzubinden:
spring:
devtools:
add-properties: true
datasource:
url: jdbc:h2:file:./instance/heat-convection-db
driver-class-name: org.h2.Driver
username: sa
password:
jpa:
hibernate:
ddl-auto: validate
logging:
level:
root: INFO
"[de.meshingbytes.heat_convection_app]": DEBUG
"[org.hibernate.SQL]": DEBUG
"[org.springframework.web]": DEBUG
"[org.hibernate.orm.jdbc.bind]": TRACE
jpa.hibernate.ddl-auto=validate überschreibt den
Wert aus der Default-Konfiguration. Dadurch wird beim Start
geprüft, ob das Datenbankschema mit den definierten JPA-Entities
kompatibel ist.
Während der Entwicklung nutze ich die Spring Boot DevTools, da Änderungen am Code automatisch erkannt werden und die Anwendung neu gestartet wird.
Zusätzlich aktiviere ich ausführlichere Log-Ausgaben für eigene Komponenten, Spring MVC sowie erzeugte SQL-Statements.
Um die Anwendung mit dem dev-Profil zu starten, kann entweder eine Bash-Variable gesetzt werden und anschließend gestartet werden:
export SPRING_PROFILES_ACTIVE=dev
mvn spring-boot:run
oder als Startargument übergeben werden:
mvn spring-boot:run -Dspring-boot.run.profiles=dev
alternativ unter Linux:
SPRING_PROFILES_ACTIVE=dev mvn spring-boot:run
Anlegen einer README.md-Vorlage
Der Spring Intializr erzeugt keine README.md. Ich
empfehle jedoch, diese direkt zu Beginn anzulegen und zunächst
unvollständige Abschnitte mit Platzhaltern zu versehen:
> TODO: add configuration variables
Dadurch bleibt die Dokumentation während der Entwicklung sichtbar und kann schrittweise ergänzt werden.
# Heat-Convection App
## Overview
## Features
## Architecture
## Technology Stack
## Configuration
## Installation
## Database & Migrations
## REST API
## Testing
## Deployment
## Contributing
## Versioning
## License
## Attribution
Erstellen der Paketstruktur
Ebenfalls bewährt hat sich das frühzeitige ANlegen der
grundlegenden Paketstruktur, die in den späteren
Implementierungsschritten benötigt wird.Ich verwende dafür ein
einfaches helper-Skript, das ich im Projekt-Root-Verzeichnis mit
den Argumenten groupId und
artifactId ausführe.
#!/bin/bash
GROUPID="$1"
ARTIFACTID="$2"
PACKAGE_NAME="${ARTIFACTID//-/_}"
SOURCE_MAIN_PATH="src/main/java/${GROUPID//./\/}/$PACKAGE_NAME"
# main layer structure
mkdir -p -v "$SOURCE_MAIN_PATH"/{config,domain,persistence,api,exception}
# domain layer structure
mkdir -p -v "$SOURCE_MAIN_PATH"/domain/{model,service,mapper}
# persistence layer structure
mkdir -p -v "$SOURCE_MAIN_PATH"/persistence/{entity,repository,mapper}
# api layer structure
mkdir -p -v "$SOURCE_MAIN_PATH"/api/{controller,dto,mapper}
Das Anlegen der paketstruktur dient zunächst lediglich der organisatorischen Vorbereitung. Die konkrete Implementierung der einzelnen Layer erfolgt erst in den späteren Schritten.
Git initialisieren
Spring Initializr legt eine .gitignore an,
initialisiert jedoch kein Git-Repository. Dies erfolgt manuell:
git init
Für meine Projekte verwende ich einen
develop-Branch als primären Entwicklungszweig und
lege diesen direkt nach der Initialisierung an:
git checkout -b develop
Anschließend wird der initiale Projektstand versioniert:
git add .
git commit -m 'chore(init): initialize Spring Boot project'
Fazit und Ausblick
Damit steht die erste Grundkonfiguration der Anwendung sowie ein Projektgerüst, das die weiteren Implementierungsschritte vorbereitet.
├── HELP.md
├── mvnw
├── mvnw.cmd
├── pom.xml
├── README.md
└── src
├── main
│ ├── java
│ │ └── de
│ │ └── meshingbytes
│ │ └── heat_convection_app
│ │ ├── api
│ │ │ ├── controller
│ │ │ ├── dto
│ │ │ └── mapper
│ │ ├── config
│ │ ├── domain
│ │ │ ├── mapper
│ │ │ ├── model
│ │ │ └── service
│ │ ├── exception
│ │ ├── HeatConvectionAppApplication.java
│ │ └── persistence
│ │ ├── entity
│ │ ├── mapper
│ │ └── repository
│ └── resources
│ ├── application-dev.yml
│ ├── application.yml
│ ├── db
│ │ └── migration
│ ├── static
│ └── templates
└── test
└── java
└── de
└── meshingbytes
└── heat_convection_app
Im Rahmen der ersten Schritte sind einige wichtige Entscheidungen getroffen:
- grundlegender Software-Stack
- Paket- und Projektstruktur
- Konfigurations- und Profilkonzept
- Datenpersistenz mit JPA und Flyway
- Versionsverwaltung und Projektdokumentation
Im nächsten Schritt folgt die erste fachliche Implementierung. Dabei werden zunächst die Berechnungslogik für den konvektiven Wärmeübergang umgesetzt, erste Datenbankmigrationen erstellt und einfache REST-Endpunkte zur Nutzung der Berechnung und Abfrage der Stoffwerte implementiert.