Java trifft Zukunft: Wie Quarkus Architektur, Performance und Cloud-Native nahtlos verbindet

Holger Tiemeyer

Angesichts der zunehmenden Allgegenwärtigkeit digitaler Technologien, die fortlaufend und immer natürlicher in unsere Umgebung eingebettet werden, stellt sich die essenzielle Frage, welche Rolle Java in dieser zukünftigen Entwicklung von IT-Systemen spielen wird. Mark Weiser, Pionier des ubiquitären Computings, formulierte diesen technologischen Paradigmenwechsel folgendermaßen: “Die beste Technologie ist die, die wir gar nicht bemerken, weil sie auf natürliche Weise unseren Alltag bereichert.” (sinngemäß nach [LMU19]).

Ubiquitäres Computing zeigt bereits heute, wie Technologie allgegenwärtig wird und als unsichtbarer Begleiter nahtlos in unsere Umgebung integriert ist. Ein zentraler Faktor dabei ist der nahezu universelle Zugang zu Netzwerken (wie z.B. WLAN), der international immer mehr zur Standardinfrastruktur gehört. Dieser Zugang ermöglicht die Echtzeitvernetzung smarter Geräte, von Smart Homes bis hin zu Wearables, und bildet die Basis für Technologien, die intuitiv im Hintergrund arbeiten und barrierefrei ohne störende Eingriffe miteinander kommunizieren. Zudem fördert das ubiquitäre Computing eine immer engere Symbiose zwischen Mensch und Maschine, in der neue Technologien heranwachsen, die klassische Interfaces zur digitalen Welt wie Smartphones oder Tablets zunehmend ablösen. Geräte wie beispielsweise Mixed-Reality-Brillen (z.B. die Meta Quest oder Apple Vision Pro) eröffnen eine neue Form der Interaktion, bei der digitale Informationen direkt und kontextbewusst ins Sichtfeld projiziert werden. Ziel ist nicht mehr nur allgemeine Interaktion, sondern eine präzise Anpassung digitaler Inhalte an die individuellen Bedürfnisse des Nutzers. Diese Entwicklung mündet in das Konzept der Hyperpersonalisierung: Systeme reagieren in Echtzeit auf Standort, Vorlieben und Ziele und liefern so maßgeschneiderte Informationen, Unterhaltung oder Unterstützung im Alltag. Damit zeichnet sich eine Zukunft ab, in der digitale Erlebnisse nahtlos in das persönliche Leben integriert sind.

Um daher in einem zunehmend dynamischen Marktumfeld bestehen zu können, müssen Unternehmen und Organisationen nicht länger nur reagieren, sondern aktiv gestalten. Hierzu werden kurze Innovationszyklen sowie maximale Geschwindigkeit und Effizienz in der Bereitstellung neuer Dienste zum zentralen Erfolgsfaktor. Doch gerade dieser Anspruch stellt die moderne Softwareentwicklung vor ein bekanntes Dilemma: Entwickler:innen benötigen Flexibilität und Tempo, um agil auf neue Anforderungen zu reagieren, während der Betrieb Sicherheit und Stabilität gewährleisten muss. Besonders in einer Zeit, die von digitalen Plattformen und Cloud-Ökosystemen beherrscht wird, wird dieses Spannungsverhältnis immer deutlicher spürbar. DevOps versucht, diese Divergenz aufzulösen, indem es Entwicklung und Betrieb durch gemeinsame Prozesse, enge Zusammenarbeit und Automatisierung miteinander
verbindet. Dennoch bleibt die zentrale Herausforderung bestehen: die Optimierung der sogenannten Durchlaufzeit einer Anforderung von der Idee bis zur produktiven Bereitstellung.

Abbildung 1 zeigt eine horizontale Zeitachse, die den Verlauf eines Entwicklungsprozesses darstellt. Sie ist in zwei Hauptabschnitte unterteilt: Durchlauf- und Verarbeitungszeit. Die Durchlaufzeit umfasst den gesamten Zeitraum von der Entstehung einer Anforderung bis zu ihrer produktiven Bereitstellung. Innerhalb dieser Spanne markiert die Verarbeitungszeit den Teil, in dem die tatsächliche Umsetzung, also die aktive Bearbeitung der Anforderung, erfolgt. Die übrige Zeit besteht aus Warte- und Abstimmungsphasen, die den Prozess verlängern.

Durchlauf- und Verarbeitungszeit

Mit zunehmender Marktdynamik gewinnt die Fähigkeit, die Durchlaufzeit zu minimieren, weiter an Bedeutung. Moderne Cloud-Infrastrukturen bilden dafür das Fundament: Sie gewährleisten Stabilität, Sicherheit und Flexibilität und ermöglichen es, Innovationen schneller und verlässlicher umzusetzen. Entscheidend ist dabei die Balance zwischen hoher Entwicklungsgeschwindigkeit und gleichbleibender Qualität. Orientierung bietet dabei das Reaktive Manifest (siehe [REA13]), das als Leitbild für moderne, vernetzte Softwarearchitekturen dient und vier zentrale, grundlegende Prinzipien für Cloud-native Anwendungen und insbesondere für Microservice-basierte Systeme definiert:

  • Nachrichtenorientierung: Systeme kommunizieren asynchron und nutzen Nachrichten, um unabhängig zu skalieren und flexibel auf Ereignisse zu reagieren.
  • Resilienz: Anwendungen bleiben auch unter Last und im Falle von Fehlern stabil, da sie sich auf Selbstheilungsmechanismen und Isolation verlassen.
  • Elastizität: Systeme skalieren dynamisch mit der Last, sowohl horizontal, indem Komponenten hinzugefügt werden, als auch vertikal, indem Ressourcen intelligent (z.B. Replikaten, o.ä.) zugewiesen werden.
  • Reaktionsfähigkeit: Anwendungen reagieren in Echtzeit auf Nutzeranfragen und Ereignisse, was unmittelbare und konsistente Nutzererfahrungen ermöglicht.

Aufbauend auf diesen Prinzipien hat sich die reaktive Programmierung als Programmierparadigma etabliert, das speziell für die Verarbeitung von asynchronen Ereignissen und den Aufbau hochperformanter, skalierbarer Systeme entwickelt wurde. Im Gegensatz zur traditionellen imperativen Programmierung, bei der Befehle sequentiell und blockierend ausgeführt werden, setzt die reaktive Programmierung auf ein nicht-blockierendes, ereignisgesteuertes Modell. Datenflüsse und Veränderungen werden dabei als Streams behandelt, die prozessiert werden, sobald neue Ereignisse eintreffen. Dieser Ansatz ermöglicht es Anwendungen, Ressourcen effizienter zu nutzen und auf Lastspitzen dynamischer zu reagieren, da Aufgaben nicht mehr aufeinander warten müssen. Gerade im Kontext moderner Microservices-Architekturen bietet die reaktive Programmierung bedeutende Vorteile, indem sie Technologien wie Elastic Scaling und resiliente Transaktionen unterstützt. Sie bildet damit eine zentrale Brücke zwischen den Anforderungen des Reaktiven Manifests und der praktischen Implementierung moderner Cloud-basierter Systeme.

Das Zusammenspiel aus Cloud-Infrastrukturen, den Prinzipien des Reaktiven Manifests und der reaktiven Programmierung hat eine klare Zielsetzung: effiziente, widerstandsfähige und skalierbare Systeme zu schaffen, die den Anforderungen moderner Märkte gerecht werden. Um Geschwindigkeit und Stabilität gleichermaßen zu garantieren, rücken die Schlüsselkomponenten des Cloud-Native-Paradigmas, DevOps, Microservices, Containerisierung sowie Continuous Integration und Continuous Delivery (CI/CD), in den Mittelpunkt. Diese Säulen bilden die Grundlage für agile und hochverfügbare IT-Landschaften, die nicht nur schneller bereitgestellt werden können, sondern auch dynamisch auf wechselnde Lasten und Anforderungen reagieren und gleichzeitig zukünftige Paradigmen (siehe oben) ermöglichen.

Doch genau an diesem Punkt zeigte Java, lange Zeit die dominierende Sprache für (Groß-)unternehmen und Organisationen, Schwächen. Während es über Jahrzehnte durch Stabilität und breite Unterstützung überzeugte, erwies sich sein Einsatz in modernen, containerbasierten Umgebungen wie Kubernetes zunehmend als Herausforderung. Zu lange Startzeiten, ein hoher Ressourcenverbrauch und die Deployment-Komplexität machten Java-basierten Anwendungen das Leben in einer Welt von Microservices und dynamischem Autoscaling schwer. Diese Nachteile führten dazu, dass Entwickler:innen und Architekt:innen häufig auf nichthierarchische Programmierparadigmen oder schlanke Frameworks setzten, um die Anforderungen der Cloud-Native-Ära besser zu erfüllen.

Von der Schwäche zur Stärke: Wie Quarkus Java neu definiert

Quarkus, ein von Red Hat gezielt mit Blick auf containerisierte cloud-native Anwendungen entwickeltes Framework, setzt genau an diesem Punkt an. Ziel dieses Frameworks ist es, die Vorteile von Java zu erhalten und gleichzeitig die Schwächen von
Java in modernen Infrastrukturen zu überwinden um damit die Bedürfnisse der Cloud-Nativen Ära zu optimieren. Es liefert damit eine Antwort auf den zentralen Zielkonflikt von DevOps: Geschwindigkeit in der Entwicklung, ohne die Stabilität oder Sicherheit des Systems zu gefährden. Anwendungen werden bereits zur Kompilierungszeit möglichst weitgehend vorbereitet, indem Quarkus beispielsweise Entscheidungsprozesse wie Dependency Injection oder Annotation-Scans von der Laufzeit in die Build-Time verschiebt und damit schnellere, ressourcenschonendere Anwendungen ermöglicht. Das Ergebnis: Schnelle Startzeiten, ein minimaler Ressourcenverbrauch und eine drastische Verringerung der Overhead-Kosten. Dies ist insbesondere für Container, die häufig gestartet und gestoppt werden, um den Anforderungen einer elastischen Infrastruktur gerecht zu werden, ein entscheidender Vorteil.

Mithilfe der Unterstützung für GraalVM ermöglicht es Quarkus zudem Anwendungen als Native Images (nicht zu verwechseln mit Container-Images) zu kompilieren. Diese Images sind hochoptimierte Binärdateien, die ohne JVM auskommen und perfekt für containerisierte Umgebungen zugeschnitten sind: Die Startzeit solcher Anwendungen liegt häufig im Millisekundenbereich. Zusätzlich wird der Speicherverbrauch erheblich reduziert.

Traditionell wird Java-Code in Bytecode kompiliert, der dann zur Laufzeit von der JVM interpretiert oder durch Just-in-Time-Compilation (JIT) optimiert wird. Das bedeutet, die endgültige Maschinencode-Erzeugung (also der ausführbare Code für das Betriebssystem und die Hardware) erfolgt, während die Anwendung läuft. Dieser Ansatz ist flexibel, kostet jedoch Startzeit und Ressourcen, da die JVM zunächst geladen und initialisiert werden muss.

Bei der Erstellung eines Native Images durch GraalVM dagegen wird der Java-Code nicht nur zu Bytecode kompiliert, sondern zur Build-Zeit vollständig in ausführbaren Maschinencode umgewandelt. Das Ergebnis ist eine eigenständige, native Binärdatei, die direkt vom Betriebssystem ausgeführt werden kann, ohne dass eine JVM erforderlich ist. Dies ist möglich, weil die Native Image-Kompilierung statisch alle zur Laufzeit benötigten Klassen, Methoden und Frameworks analysiert und integriert.

Ein Native Image kann als ausführbare Binärdatei in ein Container-Image integriert werden. Der Vorteil davon ist, dass das Container-Image dank der nativen Binärdatei viel weniger Abhängigkeiten mitbringen muss. Zum Beispiel benötigt das Image keine JVM, da das Native Image den Java-Code bereits ohne sie ausführen kann. Dies führt zu kleineren Container-Images, schnelleren Container-Starts und geringerem Ressourcenverbrauch, was essenziell in stark skalierenden und flexiblen Umgebungen wie Kubernetes ist.

Ein Blick auf die Grundprinzipien und den Aufbau von Quarkus

Um das Potenzial von Quarkus vollständig zu nutzen, ist ein Blick auf die inneren Mechanismen dieses modernen Frameworks lohnenswert. Ein solides Verständnis
seiner Funktionsweise ermöglicht einen gezielten, effizienten Einsatz und eine maßgeschneiderte Anpassung an individuelle Anforderungen. Quarkus stützt sich auf zwei zentrale Prinzipien: First-Class Kubernetes-Native Java und Developer Joy. Der erste Grundgedanke betont hohe Leistungsfähigkeit, reibungslose Integration in moderne Cloud-Umgebungen und eine optimierte Containerisierung. Der zweite legt den Fokus auf die Entwicklererfahrung, indem er eine produktivere und angenehmere Arbeitsweise fördert.

Im Zentrum der Quarkus-Architektur stehen zwei eng miteinander verknüpfte Elemente: Eclipse Vert.x und die Quarkus Extensions. Gemeinsam setzen sie die Prinzipien des Reaktiven Manifests um und schaffen eine Plattform, die sowohl hohe Flexibilität als auch außergewöhnliche Performance bietet.

Bausteine der Quarkus-Architektur

Der Quarkus Core bildet das technische Fundament. Er umfasst zentrale Werkzeuge wie Jandex, ein Framework zur schnellen Analyse und Verarbeitung von Annotationen im Bytecode, sowie Gizmo, eine Bibliothek zur Bytecode-Generierung während des Builds. Ergänzt wird dies durch das Graal SDK, das Anwendungen für die Erstellung von Native Images vorbereitet. Diese Komponenten ermöglichen es Quarkus, bereits zur Build-Zeit Code zu analysieren, anzupassen und zu optimieren.

Darauf aufbauend sorgt Eclipse Vert.x als nicht-blockierende, ereignisgesteuerte Laufzeit für die reaktive Leistungsfähigkeit von Quarkus. Es ermöglicht die asynchrone Verarbeitung von Datenströmen und Ereignissen, wodurch Ressourcen optimal genutzt und Prozesse parallel ausgeführt werden können. Anfragen, Datenflüsse und Nachrichten werden als kontinuierliche Streams behandelt, die in Echtzeit verarbeitet werden. Quarkus abstrahiert dabei die technische Komplexität und stellt Entwickler:innen eine einheitliche, reaktive Programmierbasis bereit.

Abgerundet wird die Architektur durch das System der Quarkus Extensions, die die Integration externer Technologien und Dienste (beispielsweise Datenbanken, Sicherheitslösungen oder Monitoring-Werkzeuge) vereinfachen. Sie standardisieren den Umgang mit diesen Komponenten, automatisieren Konfigurationen und verlagern viele Aufgaben in die Build-Phase. So entstehen leichtgewichtige, optimierte Anwendungen, die im Entwicklungsmodus maximale Flexibilität bieten und im Produktionsmodus auf Stabilität und Effizienz ausgelegt sind. Diese Extensions bilden die entscheidende Verbindung zwischen Entwicklung und Betrieb und tragen maßgeblich dazu bei, beide Welten nahtlos zu vereinen. Ihr Verhalten passt sich dabei dynamisch an den jeweiligen Einsatzkontext an. Diese klare Trennung zwischen Entwicklungsmodus und Produktionsmodus gehört zu den zentralen Prinzipien von Quarkus und sorgt für ein ausgewogenes Zusammenspiel von Agilität und Stabilität.

Im Entwicklungsmodus liegt der Fokus auf Schnelligkeit, Flexibilität und unmittelbarem Feedback, um den kreativen Prozess und kurze Iterationszyklen zu fördern. Der Produktionsmodus hingegen ist auf Effizienz, Zuverlässigkeit und optimale Laufzeitbedingungen ausgelegt. Diese duale Ausrichtung bildet die Grundlage für konsistente Deployments und einen reibungslosen Übergang von der Entwicklung in den Betrieb. Sie steht daher im Einklang mit modernen DevOps-Praktiken.

Im Folgenden werden beide Modi im Detail betrachtet, um zu zeigen, wie Quarkus in jeder Phase des Software-Lebenszyklus gezielt unterschiedliche Anforderungen unterstützt.

Extensions im Dev-Modus: Effizienz durch automatische Containerbereitstellung

Eine der herausragendsten Eigenschaften von Quarkus im Entwicklungsmodus ist die Fähigkeit, mithilfe von Dev-Services externe Dienste wie beispielsweise Datenbanken (z.B. PostgreSQL), Identity-Provider (z.B. Keycloak) oder Message-Broker (z.B. Kafka), uvm. automatisch und containerisiert bereitzustellen, falls diese nicht bereits vorhanden sind. Dieses Prinzip wurde entwickelt, um Entwickler:innen eine sofort einsatzbereite Umgebung zu bieten und sie von der zeitaufwändigen Initialisierung und Konfiguration solcher Dienste zu entlasten.

Sobald eine Quarkus-Extension eingebunden wird, beispielsweise die Datenbankerweiterung quarkus-jdbc-postgresql, analysiert Quarkus, ob die benötigte Ressource bereits verfügbar ist. Fehlt beispielsweise eine aktive PostgreSQL-Instanz, startet Quarkus automatisch einen passenden Docker-Container, der die Ressource bereitstellt. Dabei wird die Laufzeitumgebung mit Docker (oder Podman) orchestriert, sofern diese auf der Entwicklungsmaschine installiert sind. Die Verbindung zur gestarteten Container-Instanz wird ebenfalls automatisch konfiguriert. Entwickler:innen müssen sich lediglich auf ihre Anwendung konzentrieren, da Quarkus die Bereitstellung und Konfiguration vollständig übernimmt.

Die Kommunikation mit dem gestarteten Container erfolgt über die üblichen Quarkus-Konfigurationsmechanismen, wie etwa die Datei application.properties, die im Verzeichnis src/main/resources des Quarkus-Projekts liegt. Diese Datei erlaubt es, Parameter wie Datenbank-URLs oder Zugangsdaten dynamisch zu definieren. Quarkus aktualisiert die Konfiguration dabei automatisch, sodass weder manuelle Anpassungen noch Neustarts erforderlich sind. Dies sorgt für einen flüssigen Entwicklungsprozess und fördert die Produktivität.

Ein weiterer Vorteil dieser Integration im Dev-Modus ist die konsistente Umgebung, die Quarkus bereitstellt. Entwickler:innen können sicher sein, dass die automatisch gestarteten Dienste die späteren produktiven Bedingungen wie Datenbankversionen oder API-Standards genau abbilden. So werden später auftretende Konflikte von vornherein minimiert.

Auf technischer Ebene lässt sich das Verhalten der Dev-Services über spezifische Konfigurationsparameter steuern. Zum Beispiel ist es möglich, Dev-Services bei Bedarf zu deaktivieren und stattdessen eine bereits bestehende Infrastruktur zu verwenden:

quarkus.datasource.devservices.enabled=true

Dieser Konfigurationswert wird in der Datei application.properties angegeben. Dabei ist true der Default-Wert.

Im Dev-Modus von Quarkus wird nicht nur die Entwicklung durch die automatische Bereitstellung von Diensten über Dev-Services unterstützt, sondern auch das Testen von Anwendungen durch die Integration von Testcontainers (siehe [TCS23]) nahtlos abgedeckt. Testcontainers ist eine populäre Java-Bibliothek, die es ermöglicht, Tests in realistischen, containerbasierten Umgebungen auszuführen, ohne dass zusätzliche manuelle Schritte erforderlich sind. Quarkus startet dabei automatisch die notwendigen Container für externe Dienste wie Datenbanken oder Message-Broker und schafft so eine produktionsnahe, reproduzierbare Testumgebung. Dadurch werden Entwicklungs- und Testprozesse erheblich vereinfacht, da keine separate Testinfrastruktur gepflegt werden muss.

In Quarkus lassen sich Konfigurationen gezielt für bestimmte Laufzeitprofile festlegen, etwa für dev, test oder prod. Das geschieht über Profilpräfixe wie %test in der application.properties-Datei. Wird beispielsweise eine PostgreSQL-Datenbank im Testmodus benötigt, genügt folgende Einstellung, um sie automatisch per Testcontainers bereitzustellen:

%test.quarkus.datasource.devservices.image-name=postgres:16
%test.quarkus.datasource.username=user
%test.quarkus.datasource.password=password
%test.quarkus.datasource.db-name=testdb

Mit dieser Konfiguration startet Quarkus nur während der Testausführung automatisch einen PostgreSQL-Testcontainer. Im Entwicklungs- oder Produktionsmodus bleibt die reguläre Datenbankkonfiguration aktiv. Dadurch wird, um reproduzierbare und stabile Tests zu erzeugen, eine saubere Trennung der Umgebungen gewährleistet.

Extensions im Produktionsmodus: Fokus auf externe Servicebereitstellung

Während der Dev-Modus die automatische Bereitstellung von Containern und Diensten ermöglicht, unterscheidet sich das Verhalten von Quarkus im Produktionsmodus. In diesem Modus liegt der Fokus darauf, dass externe Dienste, wie Datenbanken, Identity-Provider oder Message-Broker, bereits in der Zielumgebung verfügbar sind. Quarkus verwaltet im Produktionsmodus keine Container-Instanzen selbst, sondern stellt sicher, dass die Anwendung nahtlos mit den bereitgestellten Diensten interagieren kann.

Die Konfiguration erfolgt hierbei ebenfalls über die Datei application.properties oder Umgebungsvariablen, wodurch sich Anwendungen flexibel an unterschiedliche Umgebungen anpassen lassen. Beispielsweise können in Kubernetes Cloud-native Mechanismen wie ConfigMaps oder Secrets genutzt werden, um Zugangsdaten oder Verbindungsendpunkte bereitzustellen. Sobald die Container-Infrastruktur bereitsteht, greift Quarkus automatisch auf diese Ressourcen zu. Dadurch bleibt die Stabilität der produktiven Umgebungen gewährleistet, und gleichzeitig wird verhindert, dass Anwendungen von Quarkus selbst auf potenziell nicht abgesicherte Container angewiesen sind.

Ein weiterer Vorteil von Quarkus ist die Fähigkeit, automatisch IaC-Skripte (Infrastructure as Code) für produktive Umgebungen zu generieren. Mithilfe von spezifischen Extensions, wie etwa der Kubernetes- oder OpenShift-Extension, erstellt Quarkus YAML-Dateien, die die benötigte Infrastruktur für die Produktion beschreiben.

Entwickler:innen können diese generierten YAML-Dateien direkt in ihre Continuous-Integration- und Deployment-Pipelines (CI/CD) einfügen, was den Übergang vom Entwicklungs- zum Produktionsmodus erheblich vereinfacht und beschleunigt.

Die Quarkus Extensions sind eng mit dem Build-Time-Optimierungsprozess verzahnt, analysieren Quellcode, binden notwendige Abhängigkeiten ein und übernehmen Konfigurationsaufgaben automatisch. Nicht benötigte Komponenten werden bereits während des Builds entfernt, wodurch Anwendungen kompakter und ressourcenschonender werden. Entwickler:innen müssen sich dadurch weniger mit technischer Komplexität befassen, während Quarkus im Hintergrund die Performance optimiert und den Code auf Effizienz trimmt.

Ein wesentlicher Beitrag zur hohen Produktivität liegt in den integrierten Entwicklungswerkzeugen, die den Gedanken des Developer Joy konsequent umsetzen.
Besonders das Live-Coding hebt sich hervor: Änderungen am Code oder an Konfigurationen werden nahezu in Echtzeit übernommen, ohne dass ein Neustart erforderlich ist. So verkürzen sich Feedback-Zyklen erheblich, und der Fokus bleibt auf der eigentlichen Logik der Anwendung und nicht auf dem Build-Prozess.

Ein weiteres Element der Flexibilität bildet die application.properties-Datei, über die zentrale Einstellungen wie Ports, Datenbankverbindungen oder Messaging-Parameter definiert werden. In containerisierten Umgebungen entfaltet sie ihr volles Potenzial: Konfigurationen können dynamisch über Umgebungsvariablen angepasst werden, was fließende Übergänge zwischen Entwicklungs-, Test- und Produktionsumgebungen ermöglicht. Aufwändige manuelle Anpassungen während des Deployments entfallen damit nahezu vollständig.

Diese Automatisierung setzt sich in der engen Integration mit Docker und Kubernetes fort. Quarkus erzeugt während des Builds automatisch passende Container-Images und Deployment-Manifest-Dateien. Dadurch werden Anwendungen direkt containerbereit ausgeliefert. Dieses stellt einen erheblichen Vorteil für kontinuierliche Integrations- und Bereitstellungsprozesse (CI/CD), bei denen Geschwindigkeit und Zuverlässigkeit entscheidend sind, dar.

Trotz seiner modernen, reaktiven und Cloud-nativen Ausrichtung bleibt Quarkus mit etablierten Java-Standards wie Jakarta EE und MicroProfile vollständig kompatibel. Unternehmen können bestehende Architekturen und Codebasen weiterverwenden und schrittweise modernisieren, ohne einen kompletten Technologiewechsel vollziehen zu müssen.

Mit dieser Kombination aus modularer Architektur, Build-Time-Optimierung, reaktiver Laufzeitumgebung und entwicklerfreundlichen Werkzeugen definiert Quarkus einen neuen Standard für Cloud-native Java-Anwendungen. Es verbindet Geschwindigkeit mit Stabilität, reduziert Komplexität und bietet eine durchdachte Grundlage für moderne Softwareentwicklung. Quarkus macht Java somit effizient, skalierbar und bereit für die Anforderungen der nächsten Generation verteilter Systeme.

Reaktives Entwickeln mit Quarkus im Dev-Modus

In dem folgenden Anwendungsbeispiel soll gezeigt werden, wie sich eine simple Aufgabenverwaltung (To-Do-App) erstellen lässt, die von Anfang an auf den Prinzipien reaktiver Entwicklung basiert. Das Beispiel beschreibt die Implementierung einer kleinen REST-API, die es Nutzern ermöglicht, Tasks (z.B. To-Do-Einträge) zu erstellen, anzuzeigen und zu löschen. Statt Blockierungen durch klassische, synchrone Datenbank- und API-Aufrufe zu tolerieren, werden die reaktiven Fähigkeiten von Quarkus genutzt, um alle Anfragen effizient und nicht-blockierend zu verarbeiten. Dabei dient Hibernate Reactive als Tool zur Kommunikation mit der Datenbank, und die reaktiven APIs von Quarkus (z.B. RESTEasy Reactive) stellen sicher, dass HTTP-Methoden wie GET oder POST vollständig asynchron ausgeführt werden.

Für die folgenden Ausführungen wird vorausgesetzt, dass eine Java-Version zur Verfügung steht, Quarkus bereits installiert und Docker auf dem Entwicklungssystem aktiv ist, da Quarkus’ Dev-Services automatisch die PostgreSQL-Datenbank als Container bereitstellen werden.

Die Zielsetzung ist folgendermaßen gegeben: Es soll eine reaktive REST-API, die Tasks verwaltet und asynchron mit einer PostgreSQL-Datenbank interagiert, erstellt werden. Alle Antworten der API und Datenbankoperationen erfolgen reaktiv und schonen so die Ressourcen der Anwendung.

Im Folgenden wird das Quarkus CLI verwendet, um ein neues Projekt mit den notwendigen reaktiven Extensions zu erstellen. Diese Erweiterungen ermöglichen es uns, die gewünschte reaktive Funktionalität für REST, Hibernate und die Datenbank zu nutzen:

quarkus create app com.example.todo-reactive --extensions="resteasy-reactive, hibernate-reactive-panache, reactive-pg-client"
  • resteasy-reactive: Liefert eine reaktive Implementierung von JAX-RS für RESTful-APIs.
  • hibernate-reactive-panache: Ermöglicht nicht-blockierende ORM-Funktionalitäten auf Basis von Hibernate.
  • reactive-pg-client: Stellt einen reaktiven PostgreSQL-Client bereit, um Anfragen asynchron auszuführen.

Im folgenden Schritt wird eine Datenbank-Entität src/main/java/com/example/todo/Task.java erstellt, die mit Hibernate Reactive verarbeitet werden kann. Dank Panache-Erweiterungen ist das Arbeiten mit Datenbankmodellen klar strukturiert und bietet reaktiven Support. Der Sourcecode dieser Entität ist folgendermaßen gegeben:

package com.example.todo;

import io.smallrye.common.annotation.Blocking;
import io.quarkus.hibernate.reactive.panache.PanacheEntityBase;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class Task extends PanacheEntityBase {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Long id;
    public String description;
    public boolean completed;
}
  • PanacheEntityBase bietet eine Reihe vordefinierter Methoden wie persist() oder findAll(), die automatisch reaktive Streams zurückgeben und nicht blockierend arbeiten.
  • Hibernate Reactive sorgt dafür, dass alle Datenbankaufrufe asynchron und ressourcenschonend ausgeführt werden.

Der REST-Controller src/main/java/com/example/todo/TaskResource.java verarbeitet Anfragen wie das Abrufen aller Tasks, das Hinzufügen neuer Aufgaben und das Löschen einzelner Einträge in einer vollständig reaktiven Weise.

package com.example.todo;

import io.smallrye.mutiny.Uni; // Reaktive API für asynchrone Datenverarbeitung
import jakarta.transaction.Transactional;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import java.util.List;

@Path("/tasks")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class TaskResource {

    @GET
    public Uni<List<Task>> listAll() {
        return Task.listAll(); // Gibt eine reaktive Uni-Instanz zurück
    }

    @POST
    @Transactional
    public Uni<Task> create(Task task) {
        return task.persist().replaceWith(task); // Persistiert asynchron und gibt
        den Task zurück
    }

    @DELETE
    @Path("/{id}")
    @Transactional
    public Uni<Void> delete(@PathParam("id") Long id) {
        return Task.findById(id).onItem().ifNotNull().transformToUni(task -> 
          task.delete()).replaceWithVoid(); // Löscht die Task asynchron
    }
}

Wie zu erkennen ist, basiert die Logik dieses Controllers auf Uni, einer Klasse der Mutiny-API, die für asynchrone und reaktive Programmierung in Quarkus verwendet wird. Alle Rückgaben der Methoden sind nicht-blockierend und werden durch Hibernate Reactive asynchron verarbeitet.

Die Datei application.properties steuert die automatische Konfiguration. Für die Entwicklung wird mithilfe der Dev Services automatisch ein PostgreSQL-Container gestartet:

quarkus.datasource.db-kind=postgresql
quarkus.datasource.devservices.enabled=true
quarkus.hibernate-orm.database.generation=drop-and-create
  • Quarkus erkennt anhand der konfigurierten Datenquelle, dass eine PostgreSQL-Datenbank benötigt wird, und startet automatisch einen passenden Container.
  • Die Einstellung drop-and-create erstellt die Datenbank für jede neue Sitzung frisch, was während der Entwicklung hilfreich ist.

Die Anwendung kann nun im Dev-Modus gestartet werden:

./mvnw quarkus:dev

Quarkus startet die Anwendung samt PostgreSQL-Container durch Dev-Services automatisch. Änderungen am Code werden sofort übernommen, ohne dass ein Neustart erforderlich ist.

Dieses Beispiel zeigt, wie leicht sich eine vollständig reaktive Anwendung mit Quarkus gestalten lässt. Dank Hibernate Reactive, Panache und RESTEasy Reactive werden alle Anfragen asynchron verarbeitet, was nicht nur die Effizienz steigert, sondern auch für eine skalierbare Architektur sorgt. In Kombination mit dem Dev-Modus und den automatisierten Dev Services bietet Quarkus eine benutzerfreundliche Plattform für moderne, reaktive Softwareentwicklung.

Die strategischen Vorteile von Quarkus

Die Vorteile von Quarkus gehen weit über technische Details hinaus. Sie wirken sich direkt auf die wirtschaftliche Effizienz und Wettbewerbsfähigkeit aus. Durch die schnelle Startzeit und den geringen Ressourcenverbrauch lassen sich Betriebskosten erheblich senken. Dieses ist ein entscheidender Faktor in Cloud-Umgebungen, in denen Unternehmen häufig nach dem “Pay-as-you-go”-Modell bezahlen.

Darüber hinaus fügt sich Quarkus nahtlos in gängige DevOps-Prozesse ein. Die Build-Time-Optimierungen sind ein natürlicher Vorteil im Kontext von CI/CD-Pipelines, da sie lange Startzeiten von Tests und Deployments eliminieren. Auch die Möglichkeit, GraalVM Native Images zu nutzen, passt perfekt zu serverlosen Architekturen, bei denen kurze Startzeiten eine Voraussetzung für Kosteneffizienz darstellen.

Nicht zuletzt bietet Quarkus Entwicklern eine Plattform, um innovative Ideen schneller in die Tat umzusetzen. Die Minimierung von Overhead in der Entwicklungsphase sowie die nahtlose Integration in Cloud-native Infrastrukturen wie Kubernetes verschaffen Teams die nötige Flexibilität, um schnelle Iterationen und experimentelle Projekte zu realisieren. Quarkus überwindet damit nicht nur die Barrieren der traditionellen Java-Welt, sondern erweist sich als ein strategisches Werkzeug, das Unternehmen hilft, im Zeitalter von Microservices, Containerisierung und Cloud-Computing wettbewerbsfähig zu bleiben.

Quarkus ist daher weit mehr als ein Framework. Es ist ein mächtiges Werkzeug für jene, die Java in die Zukunft führen wollen. Und es zwingt Unternehmen und Entwickler gleichermaßen dazu, ihre Annahmen über Java zu überdenken. Denn Quarkus zeigt eindrucksvoll, dass selbst ein in die Jahre gekommenes Ökosystem wie Java stets neu erfunden werden kann, um die Anforderungen der nächsten technologischen Ära zu erfüllen.

[LMU19] https://www.um.informatik.uni-muenchen.de/aktuelles/ubiaction2018-2_3_proceedings/ubiaction_18_19_web.pdf
[REA13] https://www.reactivemanifesto.org/
[TCS23] https://testcontainers.com/

Total
0
Shares
Previous Post

LLM Context

Next Post

Code. Collaboration. Community.

Related Posts