{"id":37901,"date":"2023-10-30T11:55:12","date_gmt":"2023-10-30T10:55:12","guid":{"rendered":"https:\/\/www.happycoders.eu\/?p=37901"},"modified":"2026-06-17T08:31:12","modified_gmt":"2026-06-17T06:31:12","slug":"hexagonale-architektur-quarkus","status":"publish","type":"post","link":"https:\/\/www.happycoders.eu\/de\/software-craftsmanship\/hexagonale-architektur-quarkus\/","title":{"rendered":"Hexagonale Architektur mit Quarkus [Tutorial]"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">In diesem Artikel zeige ich dir, wie wir die Demo-Anwendung, die wir in den beiden vorangegangenen Teilen dieser Tutorial-Serie unter Verwendung der hexagonalen Architektur implementiert haben, in eine Quarkus-Anwendung migrieren \u2013 und das alles, ohne eine einzige Zeile Code im Anwendungskern zu \u00e4ndern.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"rueckblick\">R\u00fcckblick<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In Teil zwei dieser Artikelserie haben wir eine <a href=\"\/de\/software-craftsmanship\/hexagonale-architektur-java\/\">Java-Anwendung nach hexagonaler Architektur implementiert<\/a>, und in Teil drei haben wir diese um einen <a href=\"\/de\/software-craftsmanship\/ports-and-adapters-java-tutorial-db\/\">zus\u00e4tzlichen Datenbank-Adapter erweitert<\/a>, um die Daten in einer relationalen Datenbank zu persistieren, anstatt sie fl\u00fcchtig im Arbeitsspeicher zu halten.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Bisher haben wir noch kein Application Framework eingesetzt. Stattdessen haben wir einen simplen Dependency-Injection-Mechanismus selbst implementiert und alle APIs wie Jakarta Persistence und Jakarta RESTful Web Services sowie deren Implementierungen wie Hibernate und RESTEasy direkt angebunden. Daf\u00fcr war einiges an Boilerplate-Code n\u00f6tig (z. B. f\u00fcr das Bootstrapping und das Transaktionsmanagement).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die folgende Grafik zeigt die Architektur unserer Anwendung:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full_800\"><img decoding=\"async\" width=\"800\" height=\"601\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java.v4-800x601.png\" alt=\"Hexagonale Architektur der Beispiel-Anwendung\" class=\"wp-image-36240\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java.v4-800x601.png 800w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java.v4-224x168.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java.v4-336x252.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java.v4-504x379.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java.v4-672x505.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java.v4-400x301.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java.v4-600x451.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java.v4-944x709.png 944w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java.v4-1200x902.png 1200w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java.v4.png 1600w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><\/figure>\n<\/div>\n\n\n<p class=\"wp-block-paragraph\">Falls du dir noch einmal die <a href=\"\/de\/software-craftsmanship\/hexagonale-architektur\/\">Grundlagen der hexagonalen Architektur<\/a> in Erinnerung rufen willst, kannst du dies im soeben verlinkten ersten Teil der Serie tun.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das Ergebnis der bisherigen Arbeit findest du im <code>main<\/code>-Branch <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/\" target=\"_blank\">dieses GitHub-Repositories<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"worum-geht-es-in-diesem-teil\">Worum geht es in diesem Teil?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In diesem vierten Teil werden wir die einzelnen Anbindungen der Libraries durch ein Application Framework ersetzen und einiges an Boilerplate-Code entfernen k\u00f6nnen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wir setzen in diesem Teil <a rel=\"noopener\" href=\"https:\/\/quarkus.io\/\" target=\"_blank\">Quarkus<\/a> ein, da auch Quarkus auf Standard-Libraries wie Jakarta RESTful Web Services setzt, anstatt auf eigene Implementierungen wie es das Spring-Framework tut. Das wird die Migration vereinfachen. Spring werden wir im n\u00e4chsten Teil dieser Serie behandeln.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Auch in diesem Teil werden wir, wie schon im vorherigen Teil, nicht eine einzige Zeile Code im Kern der Anwendung \u2013 also in den <code>model<\/code>- und <code>application<\/code>-Modulen \u2013 ver\u00e4ndern m\u00fcssen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wir werden Schritt f\u00fcr Schritt vorgehen, wobei die Anwendung nach jedem Schritt lauff\u00e4hig sein wird:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Zun\u00e4chst werden wir die Quarkus-Dependencies einf\u00fcgen und die direkten Dependencies zu denjenigen Libraries, die von Quarkus mitgebracht werden, entfernen.<\/li>\n\n\n\n<li>Dann werden wir das <code>adapter<\/code>-Modul an Quarkus anpassen, indem wir den Dependency-Injection-Mechanismus von Quarkus verwenden werden anstatt die Abh\u00e4ngigkeiten manuell zu injizieren.<\/li>\n\n\n\n<li>Im dritten Schritt werden wir das gleiche f\u00fcr das <code>bootstrap<\/code>-Modul machen.<\/li>\n\n\n\n<li>Und im letzten Schritt werden wir die selbst implementierte Transaktionsverwaltung und den Zugriff auf die Datenbank durch die <code>@Transactional<\/code>-Annotation und die Verwendung von Panache-Repositories umstellen.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Starten wir mit Schritt 1\u2026<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"schritt-1-ersetzen-der-dependencies\">Schritt 1: Ersetzen der Dependencies<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Im ersten Schritt werden wir die in den <code>pom.xml<\/code>-Dateien definierten Dependencies anpassen, ohne etwas am Java-Code zu \u00e4ndern. So bleibt unsere Anwendung zun\u00e4chst ohne das Application Framework lauff\u00e4hig, hat aber schon einmal ein aufger\u00e4umtes Set an Dependencies.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"anpassung-der-eltern-pom-xml\">Anpassung der Eltern-pom.xml<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Wir definieren zun\u00e4chst im <code>&lt;properties&gt;<\/code>-Block der Eltern-<code>pom.xml<\/code>-Datei die Quarkus-Version, da wir diese sp\u00e4ter an zwei Stellen ben\u00f6tigen werden (dem Import der \u201eBill of Materials\u201c und der Integration des Quarkus-Maven-Plugins). An welcher Stelle du die Quarkus-Version einf\u00fcgst, ist technisch egal \u2013 der \u00dcbersichtlichkeit halber f\u00fcge ich die Version vor der PMD-Version ein:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">properties<\/span>&gt;<\/span>\n    . . .\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">quarkus.platform.version<\/span>&gt;<\/span>3.36.2<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">quarkus.platform.version<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">pmd.version<\/span>&gt;<\/span>7.25.0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">pmd.version<\/span>&gt;<\/span>\n    . . .\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">properties<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Als n\u00e4chstes m\u00fcssen wir die eben genannte \u201eBill of Materials\u201c integrieren. Eine \u201eBill of Materials\u201c ist im Grunde eine Liste, die definiert, welche Versionen von welchen Libraries zusammenpassen. In der <a href=\"https:\/\/repo1.maven.org\/maven2\/io\/quarkus\/quarkus-bom\/3.36.2\/quarkus-bom-3.36.2.pom\" target=\"_blank\" rel=\"noreferrer noopener\">Bill of Materials von Quarkus, Version 3.36.2<\/a> steht z. B. dass Hibernate in der Version 7.3.7.Final verwendet werden soll und JUnit in der Version 6.0.3.<\/p>\n\n\n\n<div class=\"wp-block-uagb-container uagb-block-8ae4ca2e alignfull uagb-is-root-container\">\n<div class=\"uagb-block-76088612 uagb-infobox__content-wrap  uagb-infobox-icon-left uagb-infobox-left uagb-infobox-stacked-mobile uagb-infobox-image-valign-top \"><div class=\"uagb-ifb-icon-wrap\"><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\"><path d=\"M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 128c17.67 0 32 14.33 32 32c0 17.67-14.33 32-32 32S224 177.7 224 160C224 142.3 238.3 128 256 128zM296 384h-80C202.8 384 192 373.3 192 360s10.75-24 24-24h16v-64H224c-13.25 0-24-10.75-24-24S210.8 224 224 224h32c13.25 0 24 10.75 24 24v88h16c13.25 0 24 10.75 24 24S309.3 384 296 384z\"><\/path><\/svg><\/div><div class=\"uagb-ifb-content\"><div class=\"uagb-ifb-title-wrap\"><\/div><p class=\"uagb-ifb-desc\">Jetzt kommt es darauf an, wo du startest:<br><br>Wenn dein Startpunkt der Code ist, den wir in den vorherigen Teilen des Tutorials gemeinsam entwickelt haben, dann hast du noch keinen <code>&lt;dependencyManagement&gt;<\/code>-Block in der <code>pom.xml<\/code>.<br><br>Wenn du das Projekt hingegen aus meinem GitHub-Repository geklont hast, dann findest du solch einen Block, der die Versionsnummern aller Dependencies zentral verwaltet.<br><br>Warum dieser Unterschied? Im Tutorial habe ich die Versionsnummern immer direkt bei den Dependencies angegeben, da ich das Tutorial auf diese Weise f\u00fcr verst\u00e4ndlicher hielt, als wenn du f\u00fcr jede Abh\u00e4ngigkeit einen Eintrag in den <code>&lt;dependencies&gt;<\/code>-Block des jeweiligen Moduls und einen weiteren Eintrag in den <code>&lt;dependencyManagement&gt;<\/code>-Block des \u00fcbergeordneten POMs h\u00e4ttest einf\u00fcgen m\u00fcssen.<\/p><\/div><\/div>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Falls du schon einen <code>&lt;dependencyManagement&gt;<\/code>-Block hast, dann ersetze ihn durch den folgenden Block. Und wenn du noch keinen hast, dann f\u00fcge den folgenden Block einfach hinzu \u2013 z. B. vor den bestehenden <code>&lt;dependencies&gt;<\/code>-Block:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependencyManagement<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependencies<\/span>&gt;<\/span>\n        <span class=\"hljs-comment\">&lt;!-- Quarkus \"Bill of Materials\" --&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>io.quarkus.platform<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>quarkus-bom<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version<\/span>&gt;<\/span>${quarkus.platform.version}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">version<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">type<\/span>&gt;<\/span>pom<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">type<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>import<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>com.tngtech.archunit<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>archunit<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version<\/span>&gt;<\/span>1.4.2<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">version<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependencies<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependencyManagement<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Der erste Eintrag f\u00fcgt dem Projekt die oben erw\u00e4hnte Bill of Materials (BoM) hinzu, und der zweite definiert die ArchUnit-Version, welche in der Quarkus-BoM nicht definiert ist.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Im <code>&lt;dependencies&gt;<\/code>-Block k\u00f6nnen wir jetzt bei JUnit und Mockito die Versionsnummern entfernen, da diese Versionen in der Bill of Materials bereits definiert sind. Die Versionen von Lombok und AssertJ hingegen sind in der BoM nicht definiert, wir m\u00fcssen sie also explizit angeben.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Hier siehst du den vollst\u00e4ndigen <code>&lt;dependencies&gt;<\/code>-Block:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependencies<\/span>&gt;<\/span>\n    <span class=\"hljs-comment\">&lt;!-- Provided scope (shared by all modules) --&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.projectlombok<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>lombok<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version<\/span>&gt;<\/span>1.18.46<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">version<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>provided<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n\n    <span class=\"hljs-comment\">&lt;!-- Test scope (shared by all modules) --&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.junit.jupiter<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>junit-jupiter<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n        <span class=\"hljs-comment\">&lt;!-- JUnit version comes from Quarkus BoM --&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>test<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.assertj<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>assertj-core<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version<\/span>&gt;<\/span>3.27.7<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">version<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>test<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.mockito<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>mockito-core<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n        <span class=\"hljs-comment\">&lt;!-- Mockito version comes from Quarkus BoM --&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>test<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependencies<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"anpassung-der-adapter-pom-xml\">Anpassung der adapter\/pom.xml<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Als n\u00e4chstes \u00e4ndern wir die Dependencies in der <code>adapter\/pom.xml<\/code>. Wir haben dort bisher z. B. Dependencies zu Jakarta RESTful Web Services, Jakarta Persistence, RESTEasy, Hibernate und dem MySQL-Treiber angegeben. Wir ersetzen jetzt all diese Libraries durch die entsprechenden Quarkus Extensions (ich zeige dir gleich, wie die <code>pom.xml<\/code> danach aussehen wird).<\/p>\n\n\n\n<div class=\"wp-block-uagb-container uagb-block-e1b508c0 alignfull uagb-is-root-container\">\n<div class=\"uagb-block-af9d0506 uagb-infobox__content-wrap  uagb-infobox-icon-left uagb-infobox-left uagb-infobox-stacked-mobile uagb-infobox-image-valign-top \"><div class=\"uagb-ifb-icon-wrap\"><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\"><path d=\"M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 128c17.67 0 32 14.33 32 32c0 17.67-14.33 32-32 32S224 177.7 224 160C224 142.3 238.3 128 256 128zM296 384h-80C202.8 384 192 373.3 192 360s10.75-24 24-24h16v-64H224c-13.25 0-24-10.75-24-24S210.8 224 224 224h32c13.25 0 24 10.75 24 24v88h16c13.25 0 24 10.75 24 24S309.3 384 296 384z\"><\/path><\/svg><\/div><div class=\"uagb-ifb-content\"><div class=\"uagb-ifb-title-wrap\"><\/div><p class=\"uagb-ifb-desc\"><a href=\"https:\/\/quarkus.io\/faq\/#what-is-a-quarkus-extension\" target=\"_blank\" rel=\"noopener\">Quarkus Extensions<\/a> sind kleine Wrapper um die eigentlichen Libraries, die diese um einige Informationen anreichern, die es erm\u00f6glichen, dass eine Quarkus-Anwendung mit <a rel=\"noopener\" href=\"https:\/\/www.graalvm.org\/\" target=\"_blank\">GraalVM<\/a> in eine <a rel=\"noopener\" href=\"https:\/\/quarkus.io\/guides\/building-native-image\" target=\"_blank\">native Anwendung<\/a> kompiliert werden kann.<\/p><\/div><\/div>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Wir f\u00fcgen auch einige Extensions hinzu, die wir aktuell noch nicht brauchen, aber im weiteren Zuge der Migration brauchen werden:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><em>Quarkus ArC<\/em> \u2013 der auf <a rel=\"noopener\" href=\"https:\/\/jakarta.ee\/specifications\/cdi\/\" target=\"_blank\">Jakarta Contexts and Dependency Injection (CDI)<\/a> basierende Dependency-Injection-Mechanismus von Quarkus.<\/li>\n\n\n\n<li><em>Panache<\/em> \u2013 eine Library, die es \u2013 \u00e4hnlich wie <a rel=\"noopener\" href=\"https:\/\/spring.io\/projects\/spring-data-jpa\/\" target=\"_blank\">Spring Data JPA<\/a> \u2013 erlaubt, schnell und einfach JPA-Repositories zu implementieren.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Wir m\u00fcssen zudem vor\u00fcbergehend zwei der alten Libraries im <code>test<\/code>-Scope bestehen lassen:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><em>RESTEasy Undertow<\/em> \u2013 damit starten wir den Undertow-Webserver, solange wir diesen noch nicht durch Quarkus ersetzt haben.<\/li>\n\n\n\n<li><em>Testcontainers\/MySQL<\/em> \u2013 damit starten wir MySQL in den Integrationstests. Quarkus verwendet zwar sp\u00e4ter auch Testcontainers, ben\u00f6tigt daf\u00fcr aber diese spezifische Dependency nicht.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Hier findest du den vollst\u00e4ndigen <code>&lt;dependencies&gt;<\/code>-Block der <code>adapter\/pom.xml<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependencies<\/span>&gt;<\/span>\n    <span class=\"hljs-comment\">&lt;!-- Internal --&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>eu.happycoders.shop<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>application<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version<\/span>&gt;<\/span>${project.version}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">version<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n\n    <span class=\"hljs-comment\">&lt;!-- External --&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>io.quarkus<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>quarkus-arc<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>io.quarkus<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>quarkus-hibernate-orm<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>io.quarkus<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>quarkus-hibernate-orm-panache<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>io.quarkus<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>quarkus-jdbc-mysql<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>io.quarkus<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>quarkus-resteasy<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>io.quarkus<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>quarkus-resteasy-jackson<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n\n    <span class=\"hljs-comment\">&lt;!-- Test scope --&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>io.quarkus<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>quarkus-junit<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>test<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>io.quarkus<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>quarkus-junit-mockito<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>test<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>io.rest-assured<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>rest-assured<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>test<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n\n    <span class=\"hljs-comment\">&lt;!-- Required temporarily during migration --&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.jboss.resteasy<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>resteasy-undertow<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>test<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.testcontainers<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>testcontainers-mysql<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>test<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n\n    <span class=\"hljs-comment\">&lt;!-- To use the \"attached test JAR\" from the \"model\" module --&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>eu.happycoders.shop<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>model<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version<\/span>&gt;<\/span>${project.version}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">version<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">classifier<\/span>&gt;<\/span>tests<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">classifier<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">type<\/span>&gt;<\/span>test-jar<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">type<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>test<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependencies<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h4 class=\"wp-block-heading\">APIs oder Implementierungen importieren?<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Falls du das komplette Tutorial bis hierhin durchgearbeitet hast, f\u00e4llt dir vielleicht auf, dass ich eine Design-Entscheidung revidiert habe:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Bisher hatten wir im <code>adapter<\/code>-Modul lediglich die Schnittstellen (wie Jakarta RESTful Web Services und Jakarta Persistence) im <code>compile<\/code>-Scope importiert und deren Implementierungen (wie RESTEasy und Hibernate) im <code>test<\/code>-Scope. Dementsprechend mussten wir dann die Implementierungen im <code>bootstrap<\/code>-Modul noch einmal im <code>runtime<\/code>-Scope importieren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die folgende Grafik demonstriert dies beispielhaft f\u00fcr Hibernate und Jakarta Persistence:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-half_600\"><img decoding=\"async\" width=\"600\" height=\"310\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-hibernate-jpa-1-600x310.png\" alt=\"Abh\u00e4ngigkeiten der Module zu Hibernate und Jakarta Persistence \u2013 bisheriges Modell\" class=\"wp-image-38287\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-hibernate-jpa-1-600x310.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-hibernate-jpa-1-224x116.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-hibernate-jpa-1-336x174.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-hibernate-jpa-1-504x260.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-hibernate-jpa-1-672x347.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-hibernate-jpa-1-400x207.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-hibernate-jpa-1-800x413.png 800w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-hibernate-jpa-1-944x488.png 944w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-hibernate-jpa-1.png 1200w\" sizes=\"(max-width: 600px) 100vw, 600px\" \/><figcaption class=\"wp-element-caption\">Abh\u00e4ngigkeiten der Module zu Hibernate und Jakarta Persistence \u2013 bisheriges Modell<\/figcaption><\/figure>\n<\/div>\n\n\n<p class=\"wp-block-paragraph\">Im Zuge der Migration auf Quarkus haben wir nun im <code>adapter<\/code>-Modul direkt die Implementierungen im <code>compile<\/code>-Scope importiert, so dass a) explizite Imports der Schnittstellen nicht mehr n\u00f6tig sind \u2013 diese bekommen wir nun als transitive Dependencies \u00fcber die Implementierungen \u2013 und b) wir die Implementierungen nicht zus\u00e4tzlich im <code>bootstrap<\/code>-Modul importieren m\u00fcssen:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-half_600\"><img decoding=\"async\" width=\"600\" height=\"310\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-hibernate-jpa-2-600x310.png\" alt=\"Abh\u00e4ngigkeiten der Module zu Hibernate und Jakarta Persistence \u2013 neues Modell\" class=\"wp-image-38288\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-hibernate-jpa-2-600x310.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-hibernate-jpa-2-224x116.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-hibernate-jpa-2-336x174.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-hibernate-jpa-2-504x260.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-hibernate-jpa-2-672x347.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-hibernate-jpa-2-400x207.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-hibernate-jpa-2-800x413.png 800w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-hibernate-jpa-2-944x488.png 944w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-hibernate-jpa-2.png 1200w\" sizes=\"(max-width: 600px) 100vw, 600px\" \/><figcaption class=\"wp-element-caption\">Abh\u00e4ngigkeiten der Module zu Hibernate und Jakarta Persistence \u2013 neues Modell<\/figcaption><\/figure>\n<\/div>\n\n\n<p class=\"wp-block-paragraph\">Welcher Ansatz ist besser?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Beide haben ihre Vor- und Nachteile. Im urspr\u00fcnglichen Ansatz haben wir im Adapter keine unn\u00f6tigen Dependencies im <code>compile<\/code>-Scope und h\u00e4tten damit im <code>bootstrap<\/code>-Modul noch die M\u00f6glichkeit, eine andere Implementierung zu w\u00e4hlen (z. B. <a href=\"https:\/\/projects.eclipse.org\/projects\/ee4j.eclipselink\" target=\"_blank\" rel=\"noopener\">EclipseLink<\/a> anstelle von Hibernate oder <a href=\"https:\/\/projects.eclipse.org\/projects\/ee4j.jersey\" target=\"_blank\" rel=\"noopener\">Jersey<\/a> anstelle von RESTEasy).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Andererseits \u2013 wie wahrscheinlich ist es, dass wir innerhalb eines Projekts unterschiedliche Implementierungen einer API verwenden wollen? Ziemlich unwahrscheinlich! Und somit k\u00f6nnen wir hier getrost die zweite Variante einsetzen, die weniger Code ben\u00f6tigt und damit \u00fcbersichtlicher und weniger fehleranf\u00e4llig ist.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die erste Variante k\u00f6nnte dann sinnvoll sein, wenn wir eine Library ver\u00f6ffentlichen wollen, die z. B. auf Jakarta Persistence basiert, die f\u00fcr Integrationstests eine bestimmte JPA-Implementierung verwendet, es aber den Nutzer:innen der Library offen l\u00e4sst, welche JPA-Implementierung sie letztendlich einsetzen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"anpassung-der-bootstrap-pom-xml\">Anpassung der bootstrap\/pom.xml<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Kommen wir nun zu den Dependencies in der <code>bootstrap\/pom.xml<\/code>. Hier k\u00f6nnen wir ersatzlos alle Dependencies im <code>runtime<\/code>-Scope streichen. Diese ben\u00f6tigen wir nicht mehr, da wir alles N\u00f6tige bereits im <code>adapter<\/code>-Modul im <code>compile<\/code>-Scope importiert haben.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Im <code>test<\/code>-Scope m\u00fcssen wir die Quarkus-Extensions f\u00fcr JUnit und Mockito hinzuf\u00fcgen:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>io.quarkus<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>quarkus-junit<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>test<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>io.quarkus<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>quarkus-junit-mockito<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>test<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Die Dependency zu <em>RESTEasy Undertow<\/em> lassen wir vor\u00fcbergehend bestehen, so dass unsere Anwendung weiterhin lauff\u00e4hig ist.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Der gesamte <code>&lt;dependencies&gt;<\/code>-Block der <code>bootstrap\/pom.xml<\/code> sieht damit wie folgt aus:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependencies<\/span>&gt;<\/span>\n    <span class=\"hljs-comment\">&lt;!-- Internal --&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>eu.happycoders.shop<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>adapter<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version<\/span>&gt;<\/span>${project.version}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">version<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-comment\">&lt;!-- The \"application\" and \"model\" modules are transitively included already;\n         but we need to include them *explicitly* so that the aggregated JaCoCo report\n         will cover them. --&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>eu.happycoders.shop<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>application<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version<\/span>&gt;<\/span>${project.version}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">version<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>eu.happycoders.shop<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>model<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version<\/span>&gt;<\/span>${project.version}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">version<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n\n    <span class=\"hljs-comment\">&lt;!-- External --&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.jboss.resteasy<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>resteasy-undertow<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n\n    <span class=\"hljs-comment\">&lt;!-- Test scope --&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>io.quarkus<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>quarkus-junit<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>test<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>io.quarkus<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>quarkus-junit-mockito<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>test<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>com.tngtech.archunit<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>archunit<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>test<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>io.rest-assured<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>rest-assured<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>test<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n\n    <span class=\"hljs-comment\">&lt;!-- To use the \"attached test JAR\" from the \"adapter\" module --&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>eu.happycoders.shop<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>adapter<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version<\/span>&gt;<\/span>${project.version}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">version<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">classifier<\/span>&gt;<\/span>tests<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">classifier<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">type<\/span>&gt;<\/span>test-jar<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">type<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>test<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependencies<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Deine <code>pom-xml<\/code>-Dateien sollten jetzt wie folgt aussehen (die Links f\u00fchren zum Zwischenstand nach Schritt 1 im GitHub-Repository):<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/f01aaa39c71c5485fb31e8c2b82838c1a3204661\/pom.xml\" target=\"_blank\" rel=\"noopener\">pom.xml<\/a> im Hauptverzeichnis<\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/f01aaa39c71c5485fb31e8c2b82838c1a3204661\/adapter\/pom.xml\" target=\"_blank\" rel=\"noopener\">adapter\/pom.xml<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/f01aaa39c71c5485fb31e8c2b82838c1a3204661\/bootstrap\/pom.xml\" target=\"_blank\" rel=\"noopener\">bootstrap\/pom.xml<\/a><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Das Projekt sollte sich jetzt compilieren lassen, und alle Tests sollten gr\u00fcn sein:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"Klartext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext\">mvn clean verify<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Klartext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Beachte, dass wir noch immer unseren eigenen Dependency-Injection-Mechanismus und unser eigenes Transaktionsmanagement verwenden. Im n\u00e4chsten Schritt werden wir den Code anpassen, um die entsprechenden Funktionalit\u00e4ten des Application Frameworks zu benutzen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"schritt-2-quarkus-dependency-injection-im-adapter-modul\">Schritt 2: Quarkus Dependency Injection im Adapter-Modul<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Eine Aufgabe, die ein Application Framework \u00fcbernimmt, ist: Dependency Injection. Bisher haben wir die Anwendung manuell \u00fcber Konstruktor-Injektion verdrahtet. Im <code>adapter<\/code>-Modul war das noch recht \u00fcbersichtlich, da wir immer nur diejenigen Adapter konstruieren mussten, die wir testen wollten, und alle Use Cases gemockt haben.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Im <code>bootstrap<\/code>-Modul hingegen mussten wir einen kompletten Launcher schreiben, der alle Services, Controller und Repositories erstellt und miteinander verbindet und dann den Undertow-Webserver startet. F\u00fcr diese Demo-Anwendung waren das etwa hundert Zeilen Code. F\u00fcr eine Unternehmensanwendung k\u00f6nnten es aber durchaus auch Tausende bis Zehntausende Zeilen sein.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"dependency-injection-mit-quarkus\">Dependency Injection mit Quarkus <\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Der Dependency-Injection-Mechanismus von Quarkus basiert auf dem CDI-Standard, d. h, wir annotieren Klassen, die das Framework instanziieren soll, mit <code>@ApplicationScoped<\/code> (es gibt noch andere Annotationen, doch die verwenden wir in diesem Tutorial nicht). <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In unserer Demo-Anwendung m\u00fcssen wir das f\u00fcr die vier Repository-Klassen aus dem Paket <code>eu.happycoders.shop.adapter.out.persistence<\/code> machen (ich zeige dir gleich, wie genau wir das machen):<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>InMemoryCartRepository<\/code><\/li>\n\n\n\n<li><code>InMemoryProductRepository<\/code><\/li>\n\n\n\n<li><code>JpaCartRepository<\/code><\/li>\n\n\n\n<li><code>JpaProductRepository<\/code><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Unsere Controller-Klassen aus dem <code>eu.happycoders.shop.adapter.in.rest<\/code>-Paket sind bereits mit Jakarta-RESTful-Web-Services-Annotationen (<code>@Path<\/code>, <code>@GET<\/code>, <code>@POST<\/code>) versehen und werden damit automatisch auch von Quarkus instanziiert.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"konfiguration-in-memory-vs-mysql-adapter\">Konfiguration In-Memory vs. MySQL-Adapter<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Einer der Vorteile von Quarkus ist, dass es bereits zur Build-Zeit (und nicht wie Spring und die meisten Jakarta-EE-Frameworks erst zur Laufzeit) die Abh\u00e4ngigkeiten zwischen den Komponenten aufl\u00f6st und Proxies generiert. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dementsprechend bietet Quarkus verschiedene Optionen, um Komponenten konfigurationsbedingt zu erstellen oder eben nicht zu erstellen, wie wir es f\u00fcr die In-Memory- bzw. MySQL-Adapter ben\u00f6tigen:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Option 1: zur Build-Zeit \u00fcber ein Build-Profil und <code>@IfBuildProfile<\/code>-Annotationen an den Repository-Implementierungen.<\/li>\n\n\n\n<li>Option 2: zur Build-Zeit \u00fcber eine Build-Property und <code>@IfBuildProperty<\/code>-Annotationen an den Repository-Implementierungen.<\/li>\n\n\n\n<li>Option 3: zur Laufzeit \u00fcber <code>@LookupIfProperty<\/code>-Annotationen an den Repository-Implementierungen.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Da die Datenbank-Verbindungseinstellungen letztlich auch erst zur Laufzeit geladen werden, entscheide ich mich f\u00fcr die dritte Variante.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dementsprechend annotieren wir unsere vier Repository-Klassen nun wie folgt:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-meta\">@LookupIfProperty<\/span>(name = <span class=\"hljs-string\">\"persistence\"<\/span>, stringValue = <span class=\"hljs-string\">\"inmemory\"<\/span>, lookupIfMissing = <span class=\"hljs-keyword\">true<\/span>)\n<span class=\"hljs-meta\">@ApplicationScoped<\/span>\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">InMemoryCartRepository<\/span> <span class=\"hljs-keyword\">implements<\/span> <span class=\"hljs-title\">CartRepository<\/span> <\/span>{\n  . . .\n}\n\n<span class=\"hljs-meta\">@LookupIfProperty<\/span>(name = <span class=\"hljs-string\">\"persistence\"<\/span>, stringValue = <span class=\"hljs-string\">\"inmemory\"<\/span>, lookupIfMissing = <span class=\"hljs-keyword\">true<\/span>)\n<span class=\"hljs-meta\">@ApplicationScoped<\/span>\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">InMemoryProductRepository<\/span> <span class=\"hljs-keyword\">implements<\/span> <span class=\"hljs-title\">ProductRepository<\/span> <\/span>{\n  . . .\n}\n\n<span class=\"hljs-meta\">@LookupIfProperty<\/span>(name = <span class=\"hljs-string\">\"persistence\"<\/span>, stringValue = <span class=\"hljs-string\">\"mysql\"<\/span>)\n<span class=\"hljs-meta\">@ApplicationScoped<\/span>\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">JpaCartRepository<\/span> <span class=\"hljs-keyword\">implements<\/span> <span class=\"hljs-title\">CartRepository<\/span> <\/span>{\n  . . .\n}\n\n<span class=\"hljs-meta\">@LookupIfProperty<\/span>(name = <span class=\"hljs-string\">\"persistence\"<\/span>, stringValue = <span class=\"hljs-string\">\"mysql\"<\/span>)\n<span class=\"hljs-meta\">@ApplicationScoped<\/span>\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">JpaProductRepository<\/span> <span class=\"hljs-keyword\">implements<\/span> <span class=\"hljs-title\">ProductRepository<\/span> <\/span>{\n  . . .\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Was bedeutet das genau?<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Wenn wir unsere Anwendung mit dem Konfigurationseintrag <code>persistence=inmemory<\/code> oder ohne <code>persistence<\/code>-Eintrag starten, werden die In-Memory-Adapter instanziiert.<\/li>\n\n\n\n<li>Wenn wir die Anwendung mit dem Konfigurationseintrag <code>persistence=mysql<\/code> starten, werden die MySQL-Adapter instanziiert.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Konfigurationseintr\u00e4ge k\u00f6nnen wir in Quarkus entsprechend dem <a href=\"https:\/\/microprofile.io\/specifications\/microprofile-config-2\/\" target=\"_blank\" rel=\"noopener\">MicroProfile-Config-Standard<\/a> definieren. Wir k\u00f6nnen sie also in die <code>application.properties<\/code> eintragen, \u00fcber Umgebungsvariablen festlegen oder als System Properties angeben.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Damit k\u00f6nnen wir unsere Anwendung also genau so konfigurieren, wie wir es auch vor der Umstellung auf Quarkus konnten (z. B. beim Start mit dem Parameter <code>-Dpersistence=mysql<\/code>).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"persistenz-konfiguration-entfernen\">Persistenz-Konfiguration entfernen<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Quarkus findet automatisch alle mit <code>@Entity<\/code> annotierten JPA-Entities. Wir k\u00f6nnen daher die Datei <code>persistence.xml<\/code> im Verzeichnis <code>resources\/META-INF<\/code> des <code>adapter<\/code>-Moduls ersatzlos l\u00f6schen \u2013 und damit auch das gesamte <code>resources<\/code>-Verzeichnis, das nun keine Dateien mehr enth\u00e4lt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das war auch schon alles, was wir am eigentlichen Adapter-Code \u00e4ndern mussten. Als n\u00e4chstes m\u00fcssen wir die Integrationstests anpassen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"quarkus-testprofile\">Quarkus-Testprofile<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Durch die Angabe <code>lookupIfMissing = true<\/code> in den <code>@LookupIfProperty<\/code>-Annotationen der In-Memory-Adapter haben wir festgelegt, dass die In-Memory-Adapter auch dann geladen werden, wenn die <code>persistence<\/code>-Property <em>nicht<\/em> gesetzt ist. In-Memory ist also der Standard (so wie es auch vor der Umstellung auf Quarkus der Fall war).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Um nun auch die MySQL-Adapter testen zu k\u00f6nnen, m\u00fcssen wir ein Quarkus-Testprofil erstellen, das die <code>persistence<\/code>-Property auf <code>mysql<\/code> setzt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dazu legen wir im Paket <code>eu.happycoders.shop.adapter<\/code> des Testverzeichnisses die Klasse <a href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/with-quarkus\/adapter\/src\/test\/java\/eu\/happycoders\/shop\/adapter\/TestProfileWithMySQL.java\" target=\"_blank\" rel=\"noopener\">TestProfileWithMySQL<\/a> an, mit folgendem Inhalt:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">TestProfileWithMySQL<\/span> <span class=\"hljs-keyword\">implements<\/span> <span class=\"hljs-title\">QuarkusTestProfile<\/span> <\/span>{\n\n  <span class=\"hljs-meta\">@Override<\/span>\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> Map&lt;String, String&gt; <span class=\"hljs-title\">getConfigOverrides<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n    <span class=\"hljs-keyword\">return<\/span> Map.of(<span class=\"hljs-string\">\"persistence\"<\/span>, <span class=\"hljs-string\">\"mysql\"<\/span>);\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Im Folgenden k\u00f6nnen wir dann Integrationstests mit <code>@TestProfile(TestProfileWithMySQL.class)<\/code> annotieren, um sie mit der Einstellung <code>persistence=mysql<\/code> zu starten.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"anpassung-der-repository-tests\">Anpassung der Repository-Tests<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Da wir gerade bei den Testprofilen sind, beginnen wir damit, die Integrationstests f\u00fcr die Repositories anzupassen. Zu den Controller-Tests kommen wir im n\u00e4chsten Abschnitt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Zur Erinnerung: F\u00fcr die Repository-Tests haben wir eine Klassenhierarchie angelegt, in der wir die eigentlichen Tests in einer abstrakten Basisklasse definiert haben und in jeweils zwei konkreten abgeleiteten Implementierungen die In-Memory- bzw. JPA-Adapter erzeugt haben:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full_800\"><img decoding=\"async\" width=\"800\" height=\"428\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java-persistence-adapter-test-class-diagram-800x428.png\" alt=\"Hierarchie der JPA-Adapter-Testklassen\n\" class=\"wp-image-36659\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java-persistence-adapter-test-class-diagram-800x428.png 800w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java-persistence-adapter-test-class-diagram-224x120.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java-persistence-adapter-test-class-diagram-336x180.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java-persistence-adapter-test-class-diagram-504x270.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java-persistence-adapter-test-class-diagram-672x360.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java-persistence-adapter-test-class-diagram-400x214.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java-persistence-adapter-test-class-diagram-600x321.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java-persistence-adapter-test-class-diagram-944x505.png 944w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java-persistence-adapter-test-class-diagram-1200x642.png 1200w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/06\/hexagonal-architecture-java-persistence-adapter-test-class-diagram.png 1600w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">Hierarchie der JPA-Adapter-Testklassen<\/figcaption><\/figure>\n<\/div>\n\n\n<p class=\"wp-block-paragraph\">Diese Struktur lassen wir bestehen, allerdings k\u00f6nnen wir einiges an Code entfernen, da wir das Erzeugen der Adapter-Instanzen jetzt dem Framework \u00fcberlassen k\u00f6nnen. Ich zeige dir im Folgenden beispielhaft die \u00c4nderungen an den Produkt-Repository-Tests. Die Cart-Repository-Tests werden analog angepasst, und ich werde am Ende des Abschnitts die entsprechenden Klassen im GitHub-Repository verlinken.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">AbstractProductRepositoryTest<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Beginnen wir mit der abstrakten Basisklasse, <code>AbstractProductRepositoryTest<\/code>. So sieht die Klasse bisher aus:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">abstract<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">AbstractProductRepositoryTest<\/span>&lt;<span class=\"hljs-title\">T<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">ProductRepository<\/span>&gt; <\/span>{\n\n  <span class=\"hljs-keyword\">private<\/span> T productRepository;\n\n  <span class=\"hljs-meta\">@BeforeEach<\/span>\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">initRepository<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n    productRepository = createProductRepository();\n  }\n\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">protected<\/span> <span class=\"hljs-keyword\">abstract<\/span> T <span class=\"hljs-title\">createProductRepository<\/span><span class=\"hljs-params\">()<\/span><\/span>;\n\n  . . .\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Wir \u00e4ndern die Zeilen bis zu den drei Punkten wie folgt ab:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">abstract<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">AbstractProductRepositoryTest<\/span> <\/span>{\n\n  <span class=\"hljs-meta\">@Inject<\/span> Instance&lt;ProductRepository&gt; productRepositoryInstance;\n\n  <span class=\"hljs-keyword\">private<\/span> ProductRepository productRepository;\n\n  <span class=\"hljs-meta\">@BeforeEach<\/span>\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">initRepository<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n    productRepository = productRepositoryInstance.get();\n  }\n\n  . . .\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Was haben wir hier im Einzelnen getan?<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u00dcber die Zeile <code>@Inject Instance&lt;ProductRepository&gt; productRepositoryInstance<\/code> lassen wir das Framework eine Instanz des <code>ProductRepository<\/code>-Interfaces injizieren. Der Umweg \u00fcber <code>Instance<\/code> ist n\u00f6tig, da die konkrete Klasse zur Compile-Zeit noch nicht bekannt ist \u2013 zur Laufzeit k\u00f6nnte das ein <code>InMemoryProductRepository<\/code> oder ein <code>JpaProductRepository<\/code> sein.<\/li>\n\n\n\n<li>In der <code>@BeforeEach<\/code>-Methode laden wir die konkrete Instanz \u00fcber die <code>Instance.get()<\/code>-Methode.<\/li>\n\n\n\n<li>Die bisher benutzte abstrakte Methode <code>createProductRepository()<\/code> ben\u00f6tigen wir nicht mehr und k\u00f6nnen sie dementsprechend l\u00f6schen.<\/li>\n\n\n\n<li>Den Typparameter der Klasse, <code>&lt;T extends ProductRepository&gt;<\/code>, ben\u00f6tigen wir ebenfalls nicht mehr \u2013 auch diesen k\u00f6nnen wir entfernen.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Du findest die angepasste Klasse hier im GitHub-Repository: <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/with-quarkus\/adapter\/src\/test\/java\/eu\/happycoders\/shop\/adapter\/out\/persistence\/AbstractProductRepositoryTest.java\" target=\"_blank\">AbstractProductRepositoryTest<\/a><\/p>\n\n\n\n<h4 class=\"wp-block-heading\">JpaProductRepositoryTest<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Kommen wir zur JPA-Implementierung des Tests, <code>JpaProductRepositoryTest<\/code>. So sieht die Klasse bisher aus:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">JpaProductRepositoryTest<\/span>\n    <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">AbstractProductRepositoryTest<\/span>&lt;<span class=\"hljs-title\">JpaProductRepository<\/span>&gt; <\/span>{\n\n  <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">static<\/span> MySQLContainer&lt;?&gt; mysql;\n  <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">static<\/span> EntityManagerFactory entityManagerFactory;\n\n  <span class=\"hljs-meta\">@BeforeAll<\/span>\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">startDatabase<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n    mysql = <span class=\"hljs-keyword\">new<\/span> MySQLContainer&lt;&gt;(DockerImageName.parse(<span class=\"hljs-string\">\"mysql:9.7\"<\/span>));\n    mysql.start();\n\n    entityManagerFactory =\n        EntityManagerFactoryFactory.createMySqlEntityManagerFactory(\n            mysql.getJdbcUrl(), <span class=\"hljs-string\">\"root\"<\/span>, <span class=\"hljs-string\">\"test\"<\/span>);\n  }\n\n  <span class=\"hljs-meta\">@Override<\/span>\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">protected<\/span> JpaProductRepository <span class=\"hljs-title\">createProductRepository<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n    <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> JpaProductRepository(entityManagerFactory);\n  }\n\n  <span class=\"hljs-meta\">@AfterAll<\/span>\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">stopDatabase<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n    entityManagerFactory.close();\n    mysql.stop();\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">All den Boilerplate-Code k\u00f6nnen wir rausschmei\u00dfen und durch lediglich zwei Annotationen ersetzen. So sieht die Klasse nach der Umstellung aus:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-meta\">@QuarkusTest<\/span>\n<span class=\"hljs-meta\">@TestProfile<\/span>(TestProfileWithMySQL<span class=\"hljs-class\">.<span class=\"hljs-keyword\">class<\/span>)\n<span class=\"hljs-title\">class<\/span> <span class=\"hljs-title\">JpaProductRepositoryTest<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">AbstractProductRepositoryTest<\/span> <\/span>{}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Ja, du siehst richtig, die Klasse enth\u00e4lt keine einzige Methode mehr! Wie haben wir das im Einzelnen erreicht?<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Die <code>createProductRepository()<\/code>-Methode haben wir in der abstrakten Basisklasse nicht mehr ben\u00f6tigt, da wir mit dem Dependency-Injection-Mechanismus des Frameworks arbeiten. Sie kann also auch aus der Implementierung entfernt werden.<\/li>\n\n\n\n<li>Um das Framework zu starten, m\u00fcssen wir die Testklasse stattdessen mit <code>@QuarkusTest<\/code> annotieren.<\/li>\n\n\n\n<li>Durch die Annotation <code>@TestProfile(TestProfileWithMySQL.class)<\/code> legen wir fest, dass Quarkus im entsprechenden Testprofil eine Instanz von <code>JpaProductRepository<\/code> anstelle von <code>InMemoryProductRepository<\/code> erzeugt (s. Abschnitt <a href=\"#konfiguration-in-memory-vs-mysql-adapter\">Konfiguration In-Memory vs. MySQL-Adapter<\/a>).<\/li>\n\n\n\n<li>Und da Quarkus erkennt, dass unser Integrationstest eine Datenbank ben\u00f6tigt, f\u00e4hrt es vollautomatisch per <a href=\"https:\/\/testcontainers.com\/\" target=\"_blank\" rel=\"noopener\">Testcontainers<\/a> und Docker eine MySQL-Datenbank hoch\u00b9. Damit k\u00f6nnen wir auch die Methoden <code>startDatabase()<\/code> und <code>stopDatabase()<\/code> entfernen.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Du findest die angepasste Klasse hier im GitHub-Repository: <a href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/with-quarkus\/adapter\/src\/test\/java\/eu\/happycoders\/shop\/adapter\/out\/persistence\/jpa\/JpaProductRepositoryTest.java\" target=\"_blank\" rel=\"noopener\">JpaProductRepositoryTest<\/a><\/p>\n\n\n\n<p class=\"hc-footnote wp-block-paragraph\">\u00b9 Dass Quarkus MySQL und nicht eine andere Datenbank starten soll, erkennt es am Import von <code>quarkus-jdbc-mysql<\/code> in der <code>pom.xml<\/code>. Quarkus startet standardm\u00e4\u00dfig MySQL 9.5. Wenn du eine andere Version starten m\u00f6chtest, kannst du das \u00fcber den Konfigurationseintrag <code>quarkus.datasource.devservices.image-name<\/code> realisieren, z. B. \u00fcber den Eintrag <code>quarkus.datasource.devservices.image-name=mysql:9.7<\/code> in der Datei <code>test\/resources\/application.properties<\/code> im <code>adapter<\/code>-Modul.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">InMemoryRepositoryTest<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Kommen wir zur In-Memory-Implementierung, <code>InMemoryRepositoryTest<\/code>, die bisher so aussieht:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">InMemoryProductRepositoryTest<\/span>\n    <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">AbstractProductRepositoryTest<\/span>&lt;<span class=\"hljs-title\">InMemoryProductRepository<\/span>&gt; <\/span>{\n\n  <span class=\"hljs-meta\">@Override<\/span>\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">protected<\/span> InMemoryProductRepository <span class=\"hljs-title\">createProductRepository<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n    <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> InMemoryProductRepository();\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Wir entfernen wieder die <code>createProductRepository()<\/code>-Methode und f\u00fcgen die <code>@QuarkusTest<\/code>-Annotation hinzu:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-meta\">@QuarkusTest<\/span>\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">InMemoryProductRepositoryTest<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">AbstractProductRepositoryTest<\/span> <\/span>{}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Eine <code>@TestProfile<\/code>-Annotation ben\u00f6tigen wir nicht, da das In-Memory-Repository standardm\u00e4\u00dfig instanziiert wird.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Du findest die angepasste Klasse hier im GitHub-Repository: <a href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/with-quarkus\/adapter\/src\/test\/java\/eu\/happycoders\/shop\/adapter\/out\/persistence\/inmemory\/InMemoryProductRepositoryTest.java\" target=\"_blank\" rel=\"noopener\">InMemoryProductRepositoryTest<\/a><\/p>\n\n\n\n<h4 class=\"wp-block-heading\">CartRepository-Tests<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Ich ermutige dich, die entsprechenden \u00c4nderungen f\u00fcr die <code>CartRepository<\/code>-Tests zu \u00dcbungszwecken einmal selbst durchzuf\u00fchren. Wenn du damit fertig bist, kannst du deine \u00c4nderungen hier mit meinen vergleichen:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/with-quarkus\/adapter\/src\/test\/java\/eu\/happycoders\/shop\/adapter\/out\/persistence\/AbstractCartRepositoryTest.java\" target=\"_blank\" rel=\"noopener\">AbstractCartRepositoryTest<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/with-quarkus\/adapter\/src\/test\/java\/eu\/happycoders\/shop\/adapter\/out\/persistence\/jpa\/JpaCartRepositoryTest.java\" target=\"_blank\" rel=\"noopener\">JpaCartRepositoryTest<\/a><\/li>\n\n\n\n<li><a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/with-quarkus\/adapter\/src\/test\/java\/eu\/happycoders\/shop\/adapter\/out\/persistence\/inmemory\/InMemoryCartRepositoryTest.java\" target=\"_blank\">InMemoryCartRepositoryTest<\/a><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"anpassung-der-controller-tests\">Anpassung der Controller-Tests<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Kommen wir zu den Controller-Tests. Auch hier werde ich dir die \u00c4nderungen wieder nur am Beispiel des Produkt-Controllers zeigen. Der Test f\u00fcr den Cart-Controller wird analog ge\u00e4ndert.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So sieht die Klasse <code>ProductsControllerTest<\/code> bisher aus:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">ProductsControllerTest<\/span> <\/span>{\n\n  <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">final<\/span> Product TEST_PRODUCT_1 = createTestProduct(euros(<span class=\"hljs-number\">19<\/span>, <span class=\"hljs-number\">99<\/span>));\n  <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">final<\/span> Product TEST_PRODUCT_2 = createTestProduct(euros(<span class=\"hljs-number\">25<\/span>, <span class=\"hljs-number\">99<\/span>));\n\n  <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">final<\/span> FindProductsUseCase findProductsUseCase = \n      mock(FindProductsUseCase<span class=\"hljs-class\">.<span class=\"hljs-keyword\">class<\/span>)<\/span>;\n\n  <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">static<\/span> UndertowJaxrsServer server;\n\n  <span class=\"hljs-meta\">@BeforeAll<\/span>\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">init<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n    server =\n        <span class=\"hljs-keyword\">new<\/span> UndertowJaxrsServer()\n            .setPort(TEST_PORT)\n            .start()\n            .deploy(\n                <span class=\"hljs-keyword\">new<\/span> Application() {\n                  <span class=\"hljs-meta\">@Override<\/span>\n                  <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> Set&lt;Object&gt; <span class=\"hljs-title\">getSingletons<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n                    <span class=\"hljs-keyword\">return<\/span> Set.of(<span class=\"hljs-keyword\">new<\/span> FindProductsController(findProductsUseCase));\n                  }\n                });\n  }\n\n  <span class=\"hljs-meta\">@AfterAll<\/span>\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">stop<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n    server.stop();\n  }\n\n  <span class=\"hljs-meta\">@BeforeEach<\/span>\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">resetMocks<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n    Mockito.reset(findProductsUseCase);\n  }\n\n  . . .\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Wir \u00e4ndern die Zeilen bis zu den drei Punkten wie folgt ab:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-meta\">@QuarkusTest<\/span>\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">ProductsControllerTest<\/span> <\/span>{\n\n  <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">final<\/span> Product TEST_PRODUCT_1 = createTestProduct(euros(<span class=\"hljs-number\">19<\/span>, <span class=\"hljs-number\">99<\/span>));\n  <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">final<\/span> Product TEST_PRODUCT_2 = createTestProduct(euros(<span class=\"hljs-number\">25<\/span>, <span class=\"hljs-number\">99<\/span>));\n\n  <span class=\"hljs-meta\">@InjectMock<\/span> FindProductsUseCase findProductsUseCase;\n\n  . . .\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Wir konnten erneut eine ganze Menge Boilerplate-Code entfernen. Im Einzelnen haben wir Folgendes getan:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Wir annotieren die Testklasse mit <code>@QuarkusTest<\/code>, um den Integrationstest mit einer laufenden Quarkus-Anwendung zu starten.<\/li>\n\n\n\n<li>Den <code>FindProductsUseCase<\/code> mocken wir nicht mehr explizit \u00fcber <code>Mockito.mock(\u2026)<\/code>, sondern durch die Annotation <code>@InjectMock<\/code>. Diese Annotation erzeugt den Mock und weist Quarkus an, die gemockte Use-Case-Instanz \u00fcberall dort zu injizieren, wo ein <code>FindProductsUseCase<\/code> ben\u00f6tigt wird.<\/li>\n\n\n\n<li>Die <code>init()<\/code>- und <code>stop()<\/code>-Methoden k\u00f6nnen wir entfernen, da wir zum einen den Undertow-Webserver nicht mehr ben\u00f6tigen und zum anderen den <code>FindProductsController<\/code> nicht mehr manuell erzeugen m\u00fcssen \u2013 durch die Jakarta-RESTful-Web-Services-Annotationen wird dieser vom Framework erkannt und automatisch instanziiert. Und \u00fcber den <code>FindProductsController<\/code>-Konstruktor wird automatisch der gemockte <code>FindProductsUseCase<\/code> injiziert.<\/li>\n\n\n\n<li>Auch die <code>resetMocks()<\/code>-Methode k\u00f6nnen wir entfernen. Quarkus erzeugt f\u00fcr jeden Test einen frischen Mock.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Zudem entfernen wir alle Aufrufe von <code>port(TEST_PORT)<\/code> aus den einzelnen Tests. Quarkus sorgt automatisch daf\u00fcr, dass REST Assured die Anwendung auf dem richtigen Port aufruft.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Versuche gerne wieder, den <code>CartsControllerTest<\/code> selbst anzupassen. Hier findest du die angepassten Testklassen im GitHub-Repository: <\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/with-quarkus\/adapter\/src\/test\/java\/eu\/happycoders\/shop\/adapter\/in\/rest\/product\/ProductsControllerTest.java\" target=\"_blank\" rel=\"noopener\">ProductsControllerTest<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/with-quarkus\/adapter\/src\/test\/java\/eu\/happycoders\/shop\/adapter\/in\/rest\/cart\/CartsControllerTest.java\" target=\"_blank\" rel=\"noopener\">CartsControllerTest<\/a><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Damit sollten wir fertig mit den Umstellungen des <code>adapter<\/code>-Moduls sein. Allerdings f\u00fchrt ein Aufruf von <code>mvn clean verify<\/code> an dieser Stelle noch zu folgender Fehlermeldung (ich habe sie auf das Wesentliche gek\u00fcrzt):<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-19\" data-shcb-language-name=\"Klartext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext\">Found 4 deployment problems:\n&#091;1] Unsatisfied dependency for type AddToCartUseCase\n     - java member: AddToCartController():addToCartUseCase\n&#091;2] Unsatisfied dependency for type EmptyCartUseCase\n     - java member: EmptyCartController():emptyCartUseCase\n&#091;3] Unsatisfied dependency for type FindProductsUseCase\n     - java member: FindProductsController():findProductsUseCase\n&#091;4] Unsatisfied dependency for type GetCartUseCase\n     - java member: GetCartController():getCartUseCase <\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-19\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Klartext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Was bedeutet das, und was m\u00fcssen wir tun, um dieses Problem zu beheben? Das erkl\u00e4re ich dir im n\u00e4chsten Abschnitt.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"definition-der-service-komponenten\">Definition der Service-Komponenten<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Die Klassen <code>AddToCartController<\/code>, <code>EmptyCartController<\/code>, <code>FindProductsController<\/code> und <code>GetCartController<\/code> haben jeweils einen Konstruktor, \u00fcber den eine Instanz von <code>AddToCartUseCase<\/code>, <code>EmptyCartUseCase<\/code>, <code>FindProductsUseCase<\/code> und <code>GetCartUseCase<\/code> injiziert werden muss.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wir ben\u00f6tigen zwar in all unseren Integrationstests nur die jeweils gemockten Komponenten, das kann Quarkus aber nicht erkennen \u2013 um die Anwendung hochzufahren, ben\u00f6tigt es daher alle Komponenten.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wir m\u00fcssen daher an dieser Stelle definieren, wie Quarkus Instanzen der Use-Case-Interfaces erzeugen kann.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Eine M\u00f6glichkeit w\u00e4re es, im <code>application<\/code>-Modul die Klassen <code>AddToCartUseService<\/code>, <code>EmptyCartUseService<\/code>, <code>FindProductsService<\/code> und <code>GetCartUseService<\/code> jeweils mit <code>@ApplicationScoped<\/code> zu annotieren. Dazu m\u00fcssten wir das <code>application<\/code>-Modul aber mit einer Dependency auf Quarkus \u201everschmutzen\u201c. Das wollen wir aber nicht tun \u2013 das <code>application<\/code>-Modul soll frei von jeglichen technischen Details bleiben!<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wir legen daher im <code>adapter<\/code>-Modul im Paket <code>eu.happycoders.shop<\/code> eine Klasse <a href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/with-quarkus\/adapter\/src\/main\/java\/eu\/happycoders\/shop\/QuarkusAppConfig.java\" target=\"_blank\" rel=\"noopener\">QuarkusAppConfig<\/a> an (der Name spielt keine Rolle), die folgenden Inhalt hat:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-20\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">QuarkusAppConfig<\/span> <\/span>{\n\n  <span class=\"hljs-meta\">@Inject<\/span> Instance&lt;CartRepository&gt; cartRepository;\n  <span class=\"hljs-meta\">@Inject<\/span> Instance&lt;ProductRepository&gt; productRepository;\n\n  <span class=\"hljs-meta\">@Produces<\/span>\n  <span class=\"hljs-meta\">@ApplicationScoped<\/span>\n  <span class=\"hljs-function\">GetCartUseCase <span class=\"hljs-title\">getCartUseCase<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n    <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> GetCartService(cartRepository.get());\n  }\n\n  <span class=\"hljs-meta\">@Produces<\/span>\n  <span class=\"hljs-meta\">@ApplicationScoped<\/span>\n  <span class=\"hljs-function\">EmptyCartUseCase <span class=\"hljs-title\">emptyCartUseCase<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n    <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> EmptyCartService(cartRepository.get());\n  }\n\n  <span class=\"hljs-meta\">@Produces<\/span>\n  <span class=\"hljs-meta\">@ApplicationScoped<\/span>\n  <span class=\"hljs-function\">FindProductsUseCase <span class=\"hljs-title\">findProductsUseCase<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n    <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> FindProductsService(productRepository.get());\n  }\n\n  <span class=\"hljs-meta\">@Produces<\/span>\n  <span class=\"hljs-meta\">@ApplicationScoped<\/span>\n  <span class=\"hljs-function\">AddToCartUseCase <span class=\"hljs-title\">addToCartUseCase<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n    <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> AddToCartService(cartRepository.get(), productRepository.get());\n  }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-20\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Die jeweils mit <code>@Produces<\/code> und <code>@ApplicationScoped<\/code> annotierten Methoden erzeugen unsere vier Services. Die Repositories, die diese \u00fcber den Konstruktor injiziert bekommen m\u00f6chten, lassen wir uns \u00fcber die zwei mit <code>@Inject<\/code>-annotierten Felder von Quarkus vorab injizieren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Quarkus erkennt alle Abh\u00e4ngigkeiten und erzeugt die Komponenten entsprechend in folgender Reihenfolge:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Zuerst die mit <code>@ApplicationScoped<\/code> annotierten In-Memory- und JPA-Repositories.<\/li>\n\n\n\n<li>Dann die \u00fcber die Producer-Methoden definierten Services.<\/li>\n\n\n\n<li>Und zuletzt die mit <code>@Path<\/code>, <code>@POST<\/code> und <code>@GET<\/code> annotierten Controller.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Jetzt sollte sich das Projekt wieder fehlerfrei kompilieren und testen lassen:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-21\" data-shcb-language-name=\"Klartext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext\">mvn clean verify<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-21\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Klartext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Zum Abschluss k\u00f6nnen wir noch die <a href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/with-quarkus\/adapter\/pom.xml\" target=\"_blank\" rel=\"noopener\">pom.xml<\/a> des <code>adapter<\/code>-Moduls aufr\u00e4umen und die Dependencies zu <code>resteasy-undertow<\/code> und <code>org.testcontainers:testcontainers-mysql<\/code> entfernen.<\/p>\n\n\n\n<div class=\"wp-block-uagb-container uagb-block-f4daec87 alignfull uagb-is-root-container\">\n<div class=\"uagb-block-6efddfdc uagb-infobox__content-wrap  uagb-infobox-icon-left uagb-infobox-left uagb-infobox-stacked-mobile uagb-infobox-image-valign-top \"><div class=\"uagb-ifb-icon-wrap\"><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\"><path d=\"M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 128c17.67 0 32 14.33 32 32c0 17.67-14.33 32-32 32S224 177.7 224 160C224 142.3 238.3 128 256 128zM296 384h-80C202.8 384 192 373.3 192 360s10.75-24 24-24h16v-64H224c-13.25 0-24-10.75-24-24S210.8 224 224 224h32c13.25 0 24 10.75 24 24v88h16c13.25 0 24 10.75 24 24S309.3 384 296 384z\"><\/path><\/svg><\/div><div class=\"uagb-ifb-content\"><div class=\"uagb-ifb-title-wrap\"><\/div><p class=\"uagb-ifb-desc\">Es l\u00e4sst sich dar\u00fcber streiten, ob die Definition der Service-Klassen in das <code>adapter<\/code>-Modul oder das <code>bootstrap<\/code>-Modul geh\u00f6rt. Da das <code>adapter<\/code>-Modul die Service-Implementierungen nicht zwingend ben\u00f6tigt (sondern nur Mocks f\u00fcr die Integrationstests), k\u00f6nnten wir die Producer-Methoden auch Dummy-Implementierungen der Use-Cases zur\u00fcckgeben lassen und dann im <code>bootstrap<\/code>-Modul die finalen Producer-Methoden definieren, die die Services erzeugen.<br><br>Das h\u00e4tte allerdings einige unsch\u00f6ne Konsequenzen: Da das <code>bootstrap<\/code>-Modul das <code>adapter<\/code>-Modul importiert und die <code>bootstrap<\/code>-Tests die <code>adapter<\/code>-Tests, w\u00e4ren die Producer-Methoden des <code>adapter<\/code>-Moduls auch im <code>bootstrap<\/code>-Modul sichtbar. Im <code>bootstrap<\/code>-Modul g\u00e4be es dann jeweils zwei Komponenten desselben Interfaces, und Quarkus lie\u00dfe sich nicht starten.<br><br>Um das aufzul\u00f6sen, h\u00e4tten wir zwei Optionen: Wir k\u00f6nnten auf den Import der <code>adapter<\/code>-Tests in die <code>bootstrap<\/code>-Tests verzichten \u2013 dann m\u00fcssten wir allerdings eine Menge Hilfsmethoden aus den <code>adapter<\/code>-Tests in den <code>bootstrap<\/code>-Tests duplizieren. Alternativ k\u00f6nnten wir zwei separate Test-Profile f\u00fcr die <code>adapter<\/code>-Tests anlegen und die <code>QuarkusAppConfig<\/code>-Klasse im <code>adapter<\/code>-Modul mit einer <code>@IfBuildProfile<\/code>-Annotation versehen.<br><br>Beide Optionen erfordern eine Menge Overhead, so dass ich mich letztlich f\u00fcr die einfachere L\u00f6sung entschieden und die Services im <code>adapter<\/code>-Modul definiert habe.<\/p><\/div><\/div>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"schritt-3-quarkus-im-bootstrap-modul\">Schritt 3: Quarkus im Bootstrap-Modul<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Wir haben zu diesem Stand eine Anwendung, deren Adapter-Klassen innerhalb von Integrationstests mit Quarkus lauff\u00e4hig sind, die aber im <code>bootstrap<\/code>-Modul noch mit einem Undertow-Webserver gestartet wird. Das ist m\u00f6glich, da wir ausschlie\u00dflich mit Jakarta-Standards gearbeitet haben.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Trotzdem wollen wir nun auch das <code>bootstrap<\/code>-Modul auf Quarkus umstellen. Das geht zum Gl\u00fcck viel schneller als die Umstellung des <code>adapter<\/code>-Moduls.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Beginnen wir mit den Tests\u2026<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"anpassung-der-end-to-end-tests\">Anpassung der End-to-End-Tests<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Ich zeige dir die notwendigen Anpassungen wieder am Beispiel des Produkt-Tests, Klasse <code>FindProductsTest<\/code>. So sieht die Klassendefinition aktuell aus:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-22\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">FindProductsTest<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">EndToEndTest<\/span> <\/span>{\n  . . .\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-22\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Wir ersetzen den Header durch:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-23\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-meta\">@QuarkusTest<\/span>\n<span class=\"hljs-meta\">@TestProfile<\/span>(TestProfileWithMySQL<span class=\"hljs-class\">.<span class=\"hljs-keyword\">class<\/span>)\n<span class=\"hljs-title\">class<\/span> <span class=\"hljs-title\">FindProductsTest<\/span> <\/span>{\n  . . .\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-23\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Was haben wir im Detail ge\u00e4ndert?<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Wir haben die <code>@QuarkusTest<\/code>-Annotation hinzugef\u00fcgt, um den Test mit einer laufenden Quarkus-Anwendung auszuf\u00fchren.<\/li>\n\n\n\n<li>Dementsprechend braucht die Testklasse nicht mehr von der Elternklasse <code>EndToEndTest<\/code> erben, die bisher f\u00fcr das Verdrahten der Komponenten und den Start der Anwendung zust\u00e4ndig war.<\/li>\n\n\n\n<li>Mit der Annotation <code>@TestProfile(TestProfileWithMySQL.class)<\/code> starten wir die Anwendung im MySQL-Modus, so dass der End-to-End-Test auch die Persistierung der Daten in der Datenbank umfasst.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Zudem entfernen wir wieder alle Aufrufe von <code>port(TEST_PORT)<\/code> aus den einzelnen Tests.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Versuche gerne wieder einmal, den <code>CartTest<\/code> selbst anzupassen. Hier findest du die angepassten Testklassen im GitHub-Repository:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/with-quarkus\/bootstrap\/src\/test\/java\/eu\/happycoders\/shop\/bootstrap\/e2e\/FindProductsTest.java\" target=\"_blank\" rel=\"noopener\">FindProductsTest<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/with-quarkus\/bootstrap\/src\/test\/java\/eu\/happycoders\/shop\/bootstrap\/e2e\/CartTest.java\" target=\"_blank\" rel=\"noopener\">CartTest<\/a><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Folgende Klassen brauchen wir nun nicht mehr, wir k\u00f6nnen sie alle l\u00f6schen:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>die ehemalige Elternklasse der Tests, <code>EndToEndTest<\/code>,<\/li>\n\n\n\n<li>die Starterklassen <code>Launcher<\/code> und <code>RestEasyUndertowShopApplication<\/code>, die die Anwendung mit dem Undertow-Webserver starten (damit enth\u00e4lt das <code>bootstrap<\/code>-Modul keinerlei Java-Code mehr),<\/li>\n\n\n\n<li>die <code>EntityManagerFactoryFactory<\/code> aus dem <code>adapter<\/code>-Modul,<\/li>\n\n\n\n<li>die Konstante <code>TEST_PORT<\/code> aus der Klasse <code>HttpTestCommons<\/code> des <code>adapter<\/code>-Moduls.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Zudem k\u00f6nnen wir die Dependency zu <code>resteasy-undertow<\/code> aus der <a href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/with-quarkus\/bootstrap\/pom.xml\" target=\"_blank\" rel=\"noopener\">pom.xml<\/a> des <code>bootstrap<\/code>-Moduls entfernen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"bean-discovery-im-multi-module-projekt-aktivieren\">Bean Discovery im Multi-Module-Projekt aktivieren<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Mit der Umstellung des <code>bootstrap<\/code>-Moduls auf Quarkus sto\u00dfen wir noch auf eine Besonderheit von Multi-Module-Projekten: Quarkus durchsucht zur Build-Zeit nur das Hauptmodul automatisch nach CDI-Beans \u2013 also das Modul, in dem das <code>quarkus-maven-plugin<\/code> konfiguriert ist, bei uns das <code>bootstrap<\/code>-Modul. Beans in anderen Modulen findet Quarkus hingegen nur dann, wenn diese einen sogenannten Jandex-Index mitbringen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Unsere mit <code>@ApplicationScoped<\/code> annotierten Repositories \u2013 und die Producer-Methoden in <code>QuarkusAppConfig<\/code> \u2013 liegen aber im <code>adapter<\/code>-Modul. Ohne weiteres Zutun w\u00fcrde Quarkus sie nicht finden, und der Build br\u00e4che mit einer <code>UnsatisfiedResolutionException<\/code> ab.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die L\u00f6sung: Wir lassen das <code>jandex-maven-plugin<\/code> beim Bauen des <code>adapter<\/code>-Moduls einen Index erzeugen. Dazu erg\u00e4nzen wir das <code>jandex-maven-plugin<\/code> im <code>&lt;plugins&gt;<\/code>-Block der <code>adapter\/pom.xml<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">&lt;build&gt;\n    &lt;plugins&gt;\n        . . .\n\n        &lt;plugin&gt;\n            &lt;groupId&gt;io.smallrye&lt;\/groupId&gt;\n            &lt;artifactId&gt;jandex-maven-plugin&lt;\/artifactId&gt;\n            &lt;version&gt;3.5.3&lt;\/version&gt;\n            &lt;executions&gt;\n                &lt;execution&gt;\n                    &lt;id&gt;make-index&lt;\/id&gt;\n                    &lt;goals&gt;\n                        &lt;goal&gt;jandex&lt;\/goal&gt;\n                    &lt;\/goals&gt;\n                &lt;\/execution&gt;\n            &lt;\/executions&gt;\n        &lt;\/plugin&gt;\n    &lt;\/plugins&gt;\n&lt;\/build&gt;<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Beim n\u00e4chsten Build schreibt das Plugin eine Datei <code>META-INF\/jandex.idx<\/code> in das <code>adapter<\/code>-JAR. Quarkus liest diesen Index aus und erkennt dar\u00fcber alle Beans des <code>adapter<\/code>-Moduls, wenn das <code>bootstrap<\/code>-Modul das <code>adapter<\/code>-Modul einbindet.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Aufruf von <code>mvn clean verify<\/code> zeigt uns, dass die angepassten Tests erfolgreich durchlaufen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Doch wie k\u00f6nnen wir unsere Quarkus-Anwendung jetzt starten \u2013 ohne die soeben gel\u00f6schten Starterklassen?<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"starten-der-quarkus-anwendung\">Starten der Quarkus-Anwendung<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Das machen wir \u00fcber das Quarkus-Maven-Plugin. Dieses erzeugt eine startbare Anwendung und bietet uns dar\u00fcber hinaus die M\u00f6glichkeit, Quarkus im sogenannten Dev-Modus zu starten.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Als erstes f\u00fcgen wir das Plugin wie folgt in den <code>&lt;plugins&gt;<\/code>-Block der Eltern-<code>pom.xml<\/code> ein:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-24\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">plugin<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>io.quarkus.platform<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>quarkus-maven-plugin<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version<\/span>&gt;<\/span>${quarkus.platform.version}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">version<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">plugin<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-24\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Als zweites f\u00fcgen wir in die <code>pom.xml<\/code> des <code>bootstrap<\/code>-Moduls den folgenden Eintrag ein (z. B. hinter dem <code>&lt;artifactId&gt;<\/code>-Eintrag):<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">&lt;packaging&gt;quarkus&lt;\/packaging&gt;<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Als drittes f\u00fcgen wir in dieselbe <code>pom.xml<\/code> den folgenden <code>&lt;build&gt;<\/code>-Block ein (z. B. hinter dem <code>&lt;dependencies&gt;<\/code>-Block):<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">&lt;build&gt;\n    &lt;plugins&gt;\n        &lt;plugin&gt;\n            &lt;groupId&gt;io.quarkus.platform&lt;\/groupId&gt;\n            &lt;artifactId&gt;quarkus-maven-plugin&lt;\/artifactId&gt;\n            &lt;extensions&gt;true&lt;\/extensions&gt;\n        &lt;\/plugin&gt;\n    &lt;\/plugins&gt;\n&lt;\/build&gt;<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Jetzt k\u00f6nnen wir die Anwendung \u00fcber das folgende Kommando im Dev-Modus starten:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-25\" data-shcb-language-name=\"Klartext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext\">mvn quarkus:dev<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-25\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Klartext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Nach wenigen Sekunden solltest du Folgendes sehen:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full_800\"><img decoding=\"async\" width=\"800\" height=\"235\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-demo-800x235.png\" alt=\"hexagonal architecture quarkus demo\" class=\"wp-image-38130\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-demo-800x235.png 800w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-demo-224x66.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-demo-336x99.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-demo-504x148.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-demo-672x197.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-demo-400x118.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-demo-600x176.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-demo-944x277.png 944w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-demo-1200x353.png 1200w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-demo.png 1600w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Du kannst jetzt die Endpunkte der Anwendung ausprobieren, so wie es im Abschnitt <a href=\"https:\/\/www.happycoders.eu\/de\/software-craftsmanship\/hexagonale-architektur-java\/#start-der-anwendung\">\u201eStart der Anwendung\u201c des zweiten Teils der Serie<\/a> beschrieben ist.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00fcr diejenigen Leser:innen, die mit Quarkus nicht vertraut sind, m\u00f6chte ich auf ein bemerkenswertes Feature des Dev-Modus hinweisen: \u00c4nderungen, die du jetzt am Code vornimmst, wirken sich sofort auf die laufende Anwendung aus \u2013 ohne dass du sie manuell neu kompilieren und neu starten musst \u2013 probier es einmal aus!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"schritt-4-transactional-und-panache\">Schritt 4: @Transactional und Panache<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Wir haben jetzt zwar eine lauff\u00e4hige Anwendung, doch eine Baustelle haben wir noch:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In den JPA-Repositories im <code>adapter<\/code>-Modul haben wir noch einiges an Boilerplate-Code zur\u00fcckgelassen \u2013 und zwar den f\u00fcr die Transaktionsverwaltung und das Laden und Speichern der Entities. Auch hier kann uns Quarkus einiges an Overhead abnehmen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"panache-repositories-fuer-crud-operationen\">Panache Repositories f\u00fcr CRUD-Operationen<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Wir erstellen zun\u00e4chst f\u00fcr unsere zwei Entity-Typen jeweils ein Panache Repository. Ein Panache Repository implementiert grundlegende CRUD-Operationen (analog zum <code>CrudRepository<\/code> von Spring Data JPA). <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wir legen folgende zwei Klassen im Paket <code>eu.happycoders.shop.adapter.out.persistence.jpa<\/code> des <code>adapter<\/code>-Moduls an:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-26\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-meta\">@ApplicationScoped<\/span>\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">JpaCartPanacheRepository<\/span>\n    <span class=\"hljs-keyword\">implements<\/span> <span class=\"hljs-title\">PanacheRepositoryBase<\/span>&lt;<span class=\"hljs-title\">CartJpaEntity<\/span>, <span class=\"hljs-title\">Integer<\/span>&gt; <\/span>{}\n\n<span class=\"hljs-meta\">@ApplicationScoped<\/span>\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">JpaProductPanacheRepository<\/span>\n    <span class=\"hljs-keyword\">implements<\/span> <span class=\"hljs-title\">PanacheRepositoryBase<\/span>&lt;<span class=\"hljs-title\">ProductJpaEntity<\/span>, <span class=\"hljs-title\">String<\/span>&gt; <\/span>{}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-26\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Beide Klassen implementieren das Interface <code>PanacheRepositoryBase<\/code> und geben als Typparameter die Typen der zu speichernden Entities und deren Prim\u00e4rschl\u00fcssel an.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"anpassung-von-jpacartrepository\">Anpassung von JpaCartRepository<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In der Klasse <code>JpaCartRepository<\/code> ersetzen wir nun die folgenden Zeilen:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-27\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">final<\/span> EntityManagerFactory entityManagerFactory;\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-title\">JpaCartRepository<\/span><span class=\"hljs-params\">(EntityManagerFactory entityManagerFactory)<\/span> <\/span>{\n  <span class=\"hljs-keyword\">this<\/span>.entityManagerFactory = entityManagerFactory;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-27\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">durch:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-28\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">final<\/span> JpaCartPanacheRepository panacheRepository;\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-title\">JpaCartRepository<\/span><span class=\"hljs-params\">(JpaCartPanacheRepository panacheRepository)<\/span> <\/span>{\n  <span class=\"hljs-keyword\">this<\/span>.panacheRepository = panacheRepository;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-28\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">und die folgenden drei Methoden:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-29\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-meta\">@Override<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">save<\/span><span class=\"hljs-params\">(Cart cart)<\/span> <\/span>{\n  <span class=\"hljs-keyword\">try<\/span> (EntityManager entityManager = entityManagerFactory.createEntityManager()) {\n    entityManager.getTransaction().begin();\n    entityManager.merge(CartMapper.toJpaEntity(cart));\n    entityManager.getTransaction().commit();\n  }\n}\n\n<span class=\"hljs-meta\">@Override<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> Optional&lt;Cart&gt; <span class=\"hljs-title\">findByCustomerId<\/span><span class=\"hljs-params\">(CustomerId customerId)<\/span> <\/span>{\n  <span class=\"hljs-keyword\">try<\/span> (EntityManager entityManager = entityManagerFactory.createEntityManager()) {\n    CartJpaEntity cartJpaEntity = \n        entityManager.find(CartJpaEntity<span class=\"hljs-class\">.<span class=\"hljs-keyword\">class<\/span>, <span class=\"hljs-title\">customerId<\/span>.<span class=\"hljs-title\">value<\/span>())<\/span>;\n    <span class=\"hljs-keyword\">return<\/span> CartMapper.toModelEntityOptional(cartJpaEntity);\n  }\n}\n\n<span class=\"hljs-meta\">@Override<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">deleteByCustomerId<\/span><span class=\"hljs-params\">(CustomerId customerId)<\/span> <\/span>{\n  <span class=\"hljs-keyword\">try<\/span> (EntityManager entityManager = entityManagerFactory.createEntityManager()) {\n    entityManager.getTransaction().begin();\n\n    CartJpaEntity cartJpaEntity = \n        entityManager.find(CartJpaEntity<span class=\"hljs-class\">.<span class=\"hljs-keyword\">class<\/span>, <span class=\"hljs-title\">customerId<\/span>.<span class=\"hljs-title\">value<\/span>())<\/span>;\n\n    <span class=\"hljs-keyword\">if<\/span> (cartJpaEntity != <span class=\"hljs-keyword\">null<\/span>) {\n      entityManager.remove(cartJpaEntity);\n    }\n\n    entityManager.getTransaction().commit();\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-29\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">durch:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-30\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-meta\">@Override<\/span>\n<span class=\"hljs-meta\">@Transactional<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">save<\/span><span class=\"hljs-params\">(Cart cart)<\/span> <\/span>{\n  panacheRepository.getEntityManager().merge(CartMapper.toJpaEntity(cart));\n}\n\n<span class=\"hljs-meta\">@Override<\/span>\n<span class=\"hljs-meta\">@Transactional<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> Optional&lt;Cart&gt; <span class=\"hljs-title\">findByCustomerId<\/span><span class=\"hljs-params\">(CustomerId customerId)<\/span> <\/span>{\n  CartJpaEntity cartJpaEntity = panacheRepository.findById(customerId.value());\n  <span class=\"hljs-keyword\">return<\/span> CartMapper.toModelEntityOptional(cartJpaEntity);\n}\n\n<span class=\"hljs-meta\">@Override<\/span>\n<span class=\"hljs-meta\">@Transactional<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">deleteByCustomerId<\/span><span class=\"hljs-params\">(CustomerId customerId)<\/span> <\/span>{\n  panacheRepository.deleteById(customerId.value());\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-30\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Wir haben das manuelle Starten und Committen der Transaktionen durch eine <code>@Transactional<\/code>-Annotation ersetzt und die umst\u00e4ndliche Verwendung des Entity Managers durch eine deutlich komfortablere Verwendung des Panache Repositories.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"anpassung-von-jpaproductrepository\">Anpassung von JpaProductRepository<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In der Klasse <code>JpaProductRepository<\/code> gehen wir analog vor \u2013 wir ersetzen die folgenden Zeilen:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-31\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">final<\/span> EntityManagerFactory entityManagerFactory;\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-title\">JpaProductRepository<\/span><span class=\"hljs-params\">(EntityManagerFactory entityManagerFactory)<\/span> <\/span>{\n  <span class=\"hljs-keyword\">this<\/span>.entityManagerFactory = entityManagerFactory;\n  createDemoProducts();\n}\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">createDemoProducts<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n  DemoProducts.DEMO_PRODUCTS.forEach(<span class=\"hljs-keyword\">this<\/span>::save);\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-31\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">durch<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-32\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">final<\/span> JpaProductPanacheRepository panacheRepository;\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-title\">JpaProductRepository<\/span><span class=\"hljs-params\">(JpaProductPanacheRepository panacheRepository)<\/span> <\/span>{\n  <span class=\"hljs-keyword\">this<\/span>.panacheRepository = panacheRepository;\n}\n\n<span class=\"hljs-meta\">@PostConstruct<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">createDemoProducts<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n  DemoProducts.DEMO_PRODUCTS.forEach(<span class=\"hljs-keyword\">this<\/span>::save);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-32\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Beachte, dass wir <code>createDemoProducts()<\/code> nicht mehr im Konstruktor aufrufen d\u00fcrfen, da die Anwendung beim Aufruf des Konstruktors noch nicht in einem Zustand ist, in dem sie \u00fcber die Panache Repositories auf die Datenbank zugreifen kann.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Stattdessen annotieren wir die <code>createDemoProducts()<\/code>-Methode mit <code>@PostConstruct<\/code>, was Quarkus anweist, die Methode aufzurufen, sobald alle Komponenten der Anwendung erzeugt und verdrahtet wurden (genauso wie man es auch in Spring machen w\u00fcrde).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Und zuletzt ersetzen wir die folgenden drei Methoden:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-33\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-meta\">@Override<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">save<\/span><span class=\"hljs-params\">(Product product)<\/span> <\/span>{\n  <span class=\"hljs-keyword\">try<\/span> (EntityManager entityManager = entityManagerFactory.createEntityManager()) {\n    entityManager.getTransaction().begin();\n    entityManager.merge(ProductMapper.toJpaEntity(product));\n    entityManager.getTransaction().commit();\n  }\n}\n\n<span class=\"hljs-meta\">@Override<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> Optional&lt;Product&gt; <span class=\"hljs-title\">findById<\/span><span class=\"hljs-params\">(ProductId productId)<\/span> <\/span>{\n  <span class=\"hljs-keyword\">try<\/span> (EntityManager entityManager = entityManagerFactory.createEntityManager()) {\n    ProductJpaEntity jpaEntity =\n        entityManager.find(ProductJpaEntity<span class=\"hljs-class\">.<span class=\"hljs-keyword\">class<\/span>, <span class=\"hljs-title\">productId<\/span>.<span class=\"hljs-title\">value<\/span>())<\/span>;\n    <span class=\"hljs-keyword\">return<\/span> ProductMapper.toModelEntityOptional(jpaEntity);\n  }\n}\n\n<span class=\"hljs-meta\">@Override<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> List&lt;Product&gt; <span class=\"hljs-title\">findByNameOrDescription<\/span><span class=\"hljs-params\">(String queryString)<\/span> <\/span>{\n  <span class=\"hljs-keyword\">try<\/span> (EntityManager entityManager = entityManagerFactory.createEntityManager()) {\n    TypedQuery&lt;ProductJpaEntity&gt; query =\n        entityManager\n            .createQuery(\n                <span class=\"hljs-string\">\"from ProductJpaEntity \"<\/span>\n                        + <span class=\"hljs-string\">\"where name like :query or description like :query\"<\/span>,\n                ProductJpaEntity<span class=\"hljs-class\">.<span class=\"hljs-keyword\">class<\/span>)\n            .<span class=\"hljs-title\">setParameter<\/span>(\"<span class=\"hljs-title\">query<\/span>\", \"%\" + <span class=\"hljs-title\">queryString<\/span> + \"%\")<\/span>;\n\n    List&lt;ProductJpaEntity&gt; entities = query.getResultList();\n\n    <span class=\"hljs-keyword\">return<\/span> ProductMapper.toModelEntities(entities);\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-33\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">durch<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-34\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-meta\">@Override<\/span>\n<span class=\"hljs-meta\">@Transactional<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">save<\/span><span class=\"hljs-params\">(Product product)<\/span> <\/span>{\n  panacheRepository.getEntityManager().merge(ProductMapper.toJpaEntity(product));\n}\n\n<span class=\"hljs-meta\">@Override<\/span>\n<span class=\"hljs-meta\">@Transactional<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> Optional&lt;Product&gt; <span class=\"hljs-title\">findById<\/span><span class=\"hljs-params\">(ProductId productId)<\/span> <\/span>{\n  ProductJpaEntity jpaEntity = panacheRepository.findById(productId.value());\n  <span class=\"hljs-keyword\">return<\/span> ProductMapper.toModelEntityOptional(jpaEntity);\n}\n\n<span class=\"hljs-meta\">@Override<\/span>\n<span class=\"hljs-meta\">@Transactional<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> List&lt;Product&gt; <span class=\"hljs-title\">findByNameOrDescription<\/span><span class=\"hljs-params\">(String queryString)<\/span> <\/span>{\n  List&lt;ProductJpaEntity&gt; entities =\n      panacheRepository\n          .find(<span class=\"hljs-string\">\"name like ?1 or description like ?1\"<\/span>, <span class=\"hljs-string\">\"%\"<\/span> + queryString + <span class=\"hljs-string\">\"%\"<\/span>)\n          .list();\n\n  <span class=\"hljs-keyword\">return<\/span> ProductMapper.toModelEntities(entities);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-34\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Auch hier konnten wir durch die Verwendung der <code>@Transactional<\/code>-Annotation und des Panache Repositories eine Menge Boilerplate-Code entfernen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Mit einem Aufruf von <code>mvn clean verify<\/code> k\u00f6nnen wir uns best\u00e4tigen lassen, dass unsere Repositories weiterhin das tun, was sie tun sollen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Hier findest du alle in diesem Schritt hinzugef\u00fcgten bzw. ge\u00e4nderten Klassen im GitHub-Repository:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/with-quarkus\/adapter\/src\/main\/java\/eu\/happycoders\/shop\/adapter\/out\/persistence\/jpa\/JpaCartPanacheRepository.java\" target=\"_blank\" rel=\"noopener\">JpaCartPanacheRepository<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/with-quarkus\/adapter\/src\/main\/java\/eu\/happycoders\/shop\/adapter\/out\/persistence\/jpa\/JpaCartRepository.java\" target=\"_blank\" rel=\"noopener\">JpaCartRepository<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/with-quarkus\/adapter\/src\/main\/java\/eu\/happycoders\/shop\/adapter\/out\/persistence\/jpa\/JpaProductPanacheRepository.java\" target=\"_blank\" rel=\"noopener\">JpaProductPanacheRepository<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/with-quarkus\/adapter\/src\/main\/java\/eu\/happycoders\/shop\/adapter\/out\/persistence\/jpa\/JpaProductRepository.java\" target=\"_blank\" rel=\"noopener\">JpaProductRepository<\/a><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"bauen-und-starten-der-anwendung-im-produktivmodus\">Bauen und Starten der Anwendung im Produktivmodus<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Bisher haben wir die Anwendung nur \u00fcber <code>mvn quarkus:dev<\/code> im Quarkus-Dev-Mode gestartet. Doch wie k\u00f6nnen wir die Anwendung im Produktivmodus bauen und starten?<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"konfiguration-des-produktivmodus\">Konfiguration des Produktivmodus<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Um unsere Anwendung im Produktivmodus zu starten, m\u00fcssen wir die Datenbankverbindung konfigurieren. Da diese von der Umgebung abh\u00e4ngen, machen wir das in der Regel \u00fcber Umgebungsvariablen. F\u00fcr unsere kleine Demo-Anwendung ist es aber einfacher, die Parameter in der <code>application.properties<\/code>-Datei zu hinterlegen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Lege dazu eine Datei <a href=\"https:\/\/github.com\/SvenWoltmann\/hexagonal-architecture-java\/blob\/with-quarkus\/bootstrap\/src\/main\/resources\/application.properties\" target=\"_blank\" rel=\"noopener\">application.properties<\/a> im Verzeichnis <code>bootstrap\/src\/main\/resources<\/code> an, mit folgendem Inhalt:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-35\" data-shcb-language-name=\"Klartext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext\">%prod.quarkus.datasource.jdbc.url=dummy\n%prod.persistence=inmemory\n\n%mysql.quarkus.datasource.jdbc.url=jdbc:mysql:\/\/localhost:3306\/shop\n%mysql.quarkus.datasource.username=root\n%mysql.quarkus.datasource.password=test\n%mysql.quarkus.hibernate-orm.schema-management.strategy=update\n%mysql.persistence=mysql\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-35\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Klartext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Mit dem Pr\u00e4fix <code>%prod<\/code> bzw. <code>%mysql<\/code> definieren wir eine Property f\u00fcr ein bestimmtes Profil. <\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Konfiguration des Produktivmodus \u2013 In-Memory-Modus<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Das Profil <code>prod<\/code> ist das Standardprofil f\u00fcr den Produktivmodus \u2013 hier wollen wir den In-Memory-Modus verwenden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Leider m\u00fcssen wir auch im In-Memory-Modus eine JDBC-URL angeben. Ohne diese w\u00fcrde Quarkus mit folgender Fehlermeldung abbrechen:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Model classes are defined for the default persistence unit but configured datasource not found: the default EntityManagerFactory will not be created.<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Quarkus w\u00fcrde sich daran st\u00f6ren, dass im Code Entity-Klassen existieren, dass aber keine Datenbankverbindung definiert ist. Das ist leider ein unsch\u00f6ner Aspekt unserer Demo-Anwendung. In einer realen Anwendung ist es eher unwahrscheinlich, dass wir JPA-Entities definieren, die Anwendung aber nicht mit einer Datenbank verbinden.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Konfiguration des Produktivmodus \u2013 MySQL-Modus<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Im Profil <code>mysql<\/code> wollen wir den MySQL-Modus verwenden und geben entsprechend die MySQL-Verbindungsdaten f\u00fcr die Testdatenbank an (die wir im Folgenden \u00fcber Docker starten werden).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Mit dem Parameter <code>quarkus.hibernate-orm.schema-management.strategy=update<\/code> definieren wir, dass Hibernate alle Datenbanktabellen automatisch erstellen und ggf. aktualisieren soll. Dieser Parameter war in den Tests und im Dev-Mode automatisch gesetzt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In Produktion sollten wir diese Einstellung eigentlich nicht verwenden und stattdessen ein Tool wie <a rel=\"noopener\" href=\"https:\/\/www.red-gate.com\/products\/flyway\/\" target=\"_blank\">Flyway<\/a> oder <a rel=\"noopener\" href=\"https:\/\/www.liquibase.com\/\" target=\"_blank\">Liquibase<\/a> einsetzen. Doch f\u00fcr unsere Demo-Anwendung halte ich den Extra-Aufwand, den das mit sich bringen w\u00fcrde, f\u00fcr \u00fcbertrieben.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"quarkus-anwendung-bauen\">Quarkus-Anwendung bauen<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Wir bauen die Anwendung \u00fcber das Quarkus-Maven-Plugin, \u00fcber folgendes Kommando:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-36\" data-shcb-language-name=\"Klartext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext\">mvn clean package<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-36\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Klartext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Danach findest du die lauff\u00e4hige Anwendung im Verzeichnis <code>bootstrap\/target\/quarkus-app<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"start-der-quarkus-anwendung-im-in-memory-modus\">Start der Quarkus-Anwendung im In-Memory-Modus<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Du kannst die Anwendung nun mit folgendem Kommando im In-Memory-Modus starten:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-37\" data-shcb-language-name=\"Klartext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext\">cd bootstrap\/target\/quarkus-app\njava -jar quarkus-run.jar<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-37\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Klartext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Du wirst eine Warnung sehen, dass die in der <code>application.properties<\/code> definierte Dummy-JDBC-URL ung\u00fcltig ist. Diese Warnung kannst du ignorieren, da sich die Anwendung in diesem Modus ja nicht mit einer Datenbank verbindet.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"start-der-quarkus-anwendung-im-mysql-modus\">Start der Quarkus-Anwendung im MySQL-Modus<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Um die Anwendung im MySQL-Modus zu starten, musst du zun\u00e4chst eine MySQL-Datenbank starten. Das kannst du mit folgendem Docker-Kommando machen (Achtung: wenn du Windows verwendest, musst du alles in eine Zeile schreiben und die Backslashes entfernen):<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-38\" data-shcb-language-name=\"Klartext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext\">docker run --name hexagon-mysql -d -p3306:3306 \\\n    -e MYSQL_DATABASE=shop -e MYSQL_ROOT_PASSWORD=test mysql:9.7<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-38\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Klartext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Danach startest du die Anwendung wie folgt:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-39\" data-shcb-language-name=\"Klartext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext\">java -jar -Dquarkus.profile=mysql quarkus-run.jar<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-39\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Klartext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"konfiguration-der-datenbankverbindung-ueber-system-properties-oder-umgebungsvariablen\">Konfiguration der Datenbankverbindung \u00fcber System Properties oder Umgebungsvariablen<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Falls deine Datenbankparameter anders sein sollten, kannst du diese in der <code>application.properties<\/code> ab\u00e4ndern, oder zur Laufzeit alternative Werte angeben.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dies geht zum einen \u00fcber System Properties, z. B. wie folgt:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-40\" data-shcb-language-name=\"Klartext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext\">java -jar \\\n    -Dquarkus.profile=mysql \\\n    -Dquarkus.datasource.jdbc.url=jdbc:mysql:\/\/&lt;hostname and port&gt;\/&lt;database name&gt; \\\n    -Dquarkus.datasource.username=&lt;your username&gt; \\\n    -Dquarkus.datasource.password=&lt;your password&gt; \\\n    quarkus-run.jar<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-40\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Klartext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Alternativ kannst du die Einstellungen auch \u00fcber Umgebungsvariablen definieren (unter Windows musst du <code>set<\/code> statt <code>export<\/code> verwenden):<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-41\" data-shcb-language-name=\"Klartext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext\">export QUARKUS_PROFILE=mysql\nexport QUARKUS_DATASOURCE_JDBC_URL=jdbc:mysql:\/\/&lt;hostname and port&gt;\/&lt;database name&gt;\nexport QUARKUS_DATASOURCE_USERNAME=&lt;your username&gt;\nexport QUARKUS_DATASOURCE_PASSWORD=&lt;your password&gt;\njava -jar quarkus-run.jar<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-41\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Klartext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"start-der-quarkus-anwendung-mit-docker\">Start der Quarkus-Anwendung mit Docker<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Du kannst die Anwendung auch sehr einfach in ein Docker-Image verpacken. Lege dazu im Verzeichnis <code>bootstrap\/src\/main\/docker<\/code> eine Datei mit dem Namen <code>Dockerfile.jvm<\/code> und mit folgendem Inhalt an:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-42\" data-shcb-language-name=\"Dockerfile\" data-shcb-language-slug=\"dockerfile\"><span><code class=\"hljs language-dockerfile\"><span class=\"hljs-keyword\">FROM<\/span> eclipse-temurin:<span class=\"hljs-number\">25<\/span>\n\n<span class=\"hljs-keyword\">ENV<\/span> LANGUAGE=<span class=\"hljs-string\">'en_US:en'<\/span>\n\n<span class=\"hljs-keyword\">COPY<\/span><span class=\"bash\"> --chown=185 target\/quarkus-app\/lib\/ \/deployments\/lib\/<\/span>\n<span class=\"hljs-keyword\">COPY<\/span><span class=\"bash\"> --chown=185 target\/quarkus-app\/*.jar \/deployments\/<\/span>\n<span class=\"hljs-keyword\">COPY<\/span><span class=\"bash\"> --chown=185 target\/quarkus-app\/app\/ \/deployments\/app\/<\/span>\n<span class=\"hljs-keyword\">COPY<\/span><span class=\"bash\"> --chown=185 target\/quarkus-app\/quarkus\/ \/deployments\/quarkus\/<\/span>\n\n<span class=\"hljs-keyword\">EXPOSE<\/span> <span class=\"hljs-number\">8080<\/span>\n<span class=\"hljs-keyword\">USER<\/span> <span class=\"hljs-number\">185<\/span>\n<span class=\"hljs-keyword\">ENV<\/span> JAVA_OPTS=<span class=\"hljs-string\">\"-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager\"<\/span>\n<span class=\"hljs-keyword\">ENV<\/span> JAVA_APP_JAR=<span class=\"hljs-string\">\"\/deployments\/quarkus-run.jar\"<\/span>\n\n<span class=\"hljs-keyword\">ENTRYPOINT<\/span><span class=\"bash\"> &#091;<span class=\"hljs-string\">\"sh\"<\/span>, <span class=\"hljs-string\">\"-c\"<\/span>, <span class=\"hljs-string\">\"java <span class=\"hljs-variable\">$JAVA_OPTS<\/span> -jar <span class=\"hljs-variable\">$JAVA_APP_JAR<\/span>\"<\/span>]<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-42\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Dockerfile<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">dockerfile<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">F\u00fchre danach im Projektverzeichnis folgendes Kommando aus:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-43\" data-shcb-language-name=\"Klartext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext\">docker build -f bootstrap\/src\/main\/docker\/Dockerfile.jvm \\\n    -t happycoders\/shop-demo bootstrap<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-43\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Klartext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Danach kannst du das Docker-Image z. B. wie folgt starten:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-44\" data-shcb-language-name=\"Klartext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext\">docker run -p 8080:8080 happycoders\/shop-demo<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-44\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Klartext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Jetzt k\u00f6nnten wir z. B. noch eine <code>docker-compose.yml<\/code>-Datei erstellen, die eine MySQL-Datenbank und unseren Shop im MySQL-Modus hochf\u00e4hrt. Doch da es sich hier um ein Tutorial f\u00fcr hexagonale Architektur handelt und nicht um ein Docker-Tutorial, \u00fcberlasse ich weitere Experimente zum Thema Docker dir.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"fazit-und-ausblick\">Fazit und Ausblick<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In diesem Teil des Tutorials \u00fcber die hexagonale Architektur haben wir unsere in den vorherigen Teilen der Serie entwickelte Demo-Shop-Anwendung auf das Quarkus-Framework migriert. Dadurch konnten wir die Dependencies vereinheitlichen und eine Menge Boilerplate Code entfernen. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Application Framework bereitet unsere Anwendung auch f\u00fcr einen m\u00f6glichen Einsatz in Produktion vor \u2013 wir k\u00f6nnten jetzt mit relativ \u00fcberschaubarem Aufwand unsere Anwendung \u201eproduction-ready\u201c machen:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Durch <a rel=\"noopener\" href=\"https:\/\/quarkus.io\/guides\/smallrye-health\" target=\"_blank\">SmallRye Health<\/a> k\u00f6nnten wir den Status der Anwendung abrufbar machen.<\/li>\n\n\n\n<li>Durch <a rel=\"noopener\" href=\"https:\/\/quarkus.io\/guides\/telemetry-micrometer\" target=\"_blank\">Micrometer Metrics<\/a> k\u00f6nnten wir Metriken zur Verf\u00fcgung stellen.<\/li>\n\n\n\n<li>Durch <a href=\"https:\/\/quarkus.io\/guides\/smallrye-fault-tolerance\" target=\"_blank\" rel=\"noopener\">SmallRye Fault Tolerance<\/a> k\u00f6nnten wir die Abh\u00e4ngigkeit auf externe Services wie die Datenbank resilienter machen.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Bei der Migration zu Quarkus hat sich erneut der gro\u00dfe Vorteil der hexagonalen Architektur gezeigt: Wir brauchten nicht eine Zeile Code im Kern der Anwendung zu \u00e4ndern \u2013 und damit ist selbst das Application Framework in der hexagonalen Architektur nur ein austauschbares technisches Detail.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Genau das werden wir uns auch im n\u00e4chsten und letzten Teil dieser Tutorial-Serie zunutze machen: Dort werden wir n\u00e4mlich <a href=\"\/de\/software-craftsmanship\/hexagonale-architektur-spring-boot\/\">Quarkus durch das Spring Framework ersetzen<\/a>.<\/p>\n<aside><p>Wenn dir der Artikel weitergeholfen hat, w\u00fcrde ich mich sehr \u00fcber eine positive Bewertung auf meinem <a href=\"https:\/\/www.provenexpert.com\/de-de\/sven-woltmann-happycoders-eu\/7smk\/\" target=\"_blank\" rel=\"noopener\">ProvenExpert-Profil<\/a> freuen. Dein Feedback hilft mir, meine Inhalte weiter zu verbessern und motiviert mich, neue informative Artikel zu schreiben.<\/p>\r\n                        <p>\ud83d\udc49 <a href=\"https:\/\/www.provenexpert.com\/de-de\/sven-woltmann-happycoders-eu\/7smk\/\" target=\"_blank\" rel=\"noopener\">Bewertung abgeben<\/a><\/p>\r\n                        <p>M\u00f6chtest du auf dem Laufenden bleiben und informiert werden, wenn neue Artikel auf HappyCoders.eu ver\u00f6ffentlicht werden? Dann <a href=\"#\" data-formkit-toggle=\"d8ee997126\">klicke hier<\/a>, um dich f\u00fcr den HappyCoders-Newsletter anzumelden.<\/p>\r\n                        <p>\ud83d\udc49 <a href=\"#\" data-formkit-toggle=\"d8ee997126\">Newsletter-Anmeldung<\/a><\/p><\/aside>","protected":false},"excerpt":{"rendered":"<p>Hexagonale-Architektur-Tutorial, Teil 4: Wir migrieren die Demo-Anwendung zu Quarkus mit CDI und Panache \u2013 ohne \u00c4nderungen im Anwendungskern.<\/p>\n","protected":false},"author":1,"featured_media":37929,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_seopress_titles_title":"","_seopress_titles_desc":"","_seopress_robots_index":"","_seopress_robots_follow":"","_seopress_robots_imageindex":"","_seopress_robots_snippet":"","_seopress_robots_primary_cat":"","_seopress_robots_breadcrumbs":"","_seopress_robots_freeze_modified_date":"","_seopress_robots_custom_modified_date":"","_seopress_robots_canonical":"","_seopress_social_fb_title":"","_seopress_social_fb_desc":"","_seopress_social_fb_img":"","_seopress_social_fb_img_attachment_id":0,"_seopress_social_fb_img_width":0,"_seopress_social_fb_img_height":0,"_seopress_social_twitter_title":"","_seopress_social_twitter_desc":"","_seopress_social_twitter_img":"","_seopress_social_twitter_img_attachment_id":0,"_seopress_social_twitter_img_width":0,"_seopress_social_twitter_img_height":0,"_seopress_redirections_value":"","_seopress_redirections_enabled":"","_seopress_redirections_enabled_regex":"","_seopress_redirections_logged_status":"","_seopress_redirections_param":"","_seopress_redirections_type":0,"_seopress_analysis_target_kw":"Hexagonale Architektur mit Quarkus","_seopress_news_disabled":"","_seopress_video_disabled":"","_seopress_video":[],"_seopress_pro_schemas_manual":[],"_seopress_pro_rich_snippets_disable_all":"","_seopress_pro_rich_snippets_disable":[],"_seopress_pro_schemas":[],"_uag_custom_page_level_css":"","_wp_convertkit_post_meta":{"form":"-1","landing_page":"","tag":"0","restrict_content":"0"},"_metis_text_type":"standard","_metis_text_length":56147,"_post_count":0,"footnotes":""},"categories":[204],"tags":[209],"class_list":["post-37901","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-software-craftsmanship","tag-hexagonale-architektur"],"uagb_featured_image_src":{"full":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-feature.jpg",1770,986,false],"thumbnail":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-feature.jpg",150,84,false],"medium":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-feature.jpg",300,167,false],"medium_large":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-feature.jpg",768,428,false],"large":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-feature.jpg",1024,570,false],"feature_thumb_224":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-feature-224x125.jpg",224,125,true],"feature_thumb_336":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-feature-336x187.jpg",336,187,true],"feature_thumb_504":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-feature-504x281.jpg",504,281,true],"feature_thumb_672":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-feature-672x374.jpg",672,374,true],"half_400":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-feature-400x223.jpg",400,223,true],"half_600":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-feature-600x334.jpg",600,334,true],"full_800":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-feature-800x446.jpg",800,446,true],"full_944":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-feature-944x526.jpg",944,526,true],"full_1200":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-feature-1200x668.jpg",1200,668,true],"wide_1180":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-feature-1180x490.jpg",1180,490,true],"wide_1770":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-feature-1770x735.jpg",1770,735,true],"1536x1536":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-feature.jpg",1536,856,false],"2048x2048":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2023\/10\/hexagonal-architecture-quarkus-feature.jpg",1770,986,false]},"uagb_author_info":{"display_name":"Sven Woltmann","author_link":"https:\/\/www.happycoders.eu\/de\/author\/sven\/"},"uagb_comment_info":2,"uagb_excerpt":"Hexagonale-Architektur-Tutorial, Teil 4: Wir migrieren die Demo-Anwendung zu Quarkus mit CDI und Panache \u2013 ohne \u00c4nderungen im Anwendungskern.","public_identification_id":"0a2f60a3fba24039a0c974da99d2807b","private_identification_id":"60fe10029f794f6f9c827c616af145ae","_links":{"self":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts\/37901","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/comments?post=37901"}],"version-history":[{"count":29,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts\/37901\/revisions"}],"predecessor-version":[{"id":57140,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts\/37901\/revisions\/57140"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/media\/37929"}],"wp:attachment":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/media?parent=37901"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/categories?post=37901"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/tags?post=37901"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}