#Lernpfad Enterprise Java: Spring, Jakarta EE & moderne Frameworks
Ziel: Dieses Lernbuch führt dich systematisch von den Grundlagen zu produktionsnahen Enterprise-Anwendungen mit Spring / Spring Boot, Jakarta EE, MicroProfile, Quarkus, Micronaut, Helidon und dem wichtigsten Enterprise-Java-Ökosystem. Zusätzlich enthält es ein komplexes, modulares Microservice-Beispielprojekt mit Maven, Code, Docker Compose und Architektur-Erklärung. Es ist als praktischer Lernpfad aufgebaut: erst verstehen, dann kleine Übungen, danach Mini-Projekte und zum Schluss Enterprise-Architektur.
Stand: Juli 2026
Zielgruppe: Java-Entwickler, Backend-Entwickler, Full-Stack-Entwickler mit Java-Schwerpunkt, Umsteiger von Java EE/Jakarta EE zu Spring oder umgekehrt, sowie Entwickler, die moderne Cloud-native-Java-Frameworks vergleichen wollen.
Voraussetzung: Java-Grundlagen, OOP, Collections, Exceptions, generics, HTTP-Grundlagen und SQL-Basiswissen.
#Inhaltsverzeichnis
- 1. Orientierung: Was du am Ende können sollst
- 2. Lernstrategie und Reihenfolge
- 3. Technisches Setup
- 4. Gemeinsame Grundlagen für Spring und Jakarta EE
- 4.1 HTTP und REST
- 4.2 Dependency Injection
- 4.3 Schichtenarchitektur
- 4.4 Transaktionen
- 5. Spring-Lernpfad
- 5.1 Modul S1: Spring Core und Dependency Injection
- 5.2 Modul S2: Spring Boot Grundlagen
- 5.3 Modul S3: REST APIs mit Spring MVC
- 5.4 Modul S4: Spring Data, JPA und Transaktionen
- 5.5 Modul S5: Spring Security
- 5.6 Modul S6: Testing in Spring
- 5.7 Modul S7: Spring für Produktion
- 5.8 Modul S8: Spring Cloud und verteilte Systeme
- 6. Jakarta-EE-Lernpfad
- 6.1 Modul J1: Jakarta EE Plattform verstehen
- 6.2 Modul J2: CDI – Contexts and Dependency Injection
- 6.3 Modul J3: REST APIs mit Jakarta REST
- 6.4 Modul J4: Jakarta Persistence und Transaktionen
- 6.5 Modul J5: Jakarta Security
- 6.6 Modul J6: Batch, Messaging und Enterprise Integration
- 6.7 Modul J7: Jakarta Deployment und Application Server
- 7. Vergleich: Spring vs. Jakarta EE
- 7.1 Denkmodell
- 7.2 Wann Spring?
- 7.3 Wann Jakarta EE?
- 7.4 Mischwelt: Spring und Jakarta
- 8. Enterprise-Architektur-Lernpfad
- 8.1 Modul A1: Architekturformen
- 8.2 Modul A2: Hexagonale Architektur
- 8.3 Modul A3: Fehlerbehandlung
- 8.4 Modul A4: Security-Architektur
- 8.5 Modul A5: Build, Release und CI/CD
- 9. Praxisprojekte
- 9.1 Projekt 1: Spring Customer Service
- 9.2 Projekt 2: Jakarta Inventory Application
- 9.3 Projekt 3: Migration Java EE / javax zu Jakarta / jakarta
- 9.4 Projekt 4: Architekturvergleich
- 10. Prüfungsfragen und Selbstkontrolle
- 10.1 Spring Fragen
- 10.2 Jakarta EE Fragen
- 10.3 Architekturfragen
- 11. Empfohlene Reihenfolge als 16-Wochen-Plan
- 12. Referenz-Templates
- 12.1 Spring Projektstruktur
- 12.2 Jakarta EE Projektstruktur
- 12.3 Maven Quality Parent Beispiel
- 12.4 Docker Compose für lokale Datenbank
- 12.5 Lernnotiz-Template
- 13. Erweiterter Framework-Kompass
- 14. MicroProfile-Lernpfad
- 15. Quarkus-Lernpfad
- 16. Micronaut-Lernpfad
- 17. Helidon-Lernpfad
- 18. Persistenz-Vertiefung: JPA, Hibernate, Spring Data, jOOQ, Flyway, Liquibase
- 19. API-Design: REST, OpenAPI, GraphQL, gRPC und Async APIs
- 20. Security-Lernpfad: Spring Security, Jakarta Security, OAuth2, OIDC, Keycloak
- 21. Messaging und Integration: Kafka, RabbitMQ, JMS, Spring Integration, Apache Camel
- 22. Testing-Lernpfad: Von Unit Tests bis Architekturtests
- 23. Observability-Lernpfad: Logs, Metriken, Traces
- 24. Cloud-native Deployment: Docker, Kubernetes, Helm und GitOps
- 25. Build, CI/CD und Qualitätssicherung
- 26. Performance und moderne JVM: Virtual Threads, GraalVM Native Image, Profiling
- 27. Architekturpatterns für Enterprise Java
- 28. Daten- und Integrationspatterns
- 29. Migration-Lernpfade
- 30. Framework-Entscheidungsmatrix
- 31. Erweiterter 24-Wochen-Lernplan
- 32. Abschlussprojekt: Enterprise Shop Plattform
- 33. Lernkarten: Was du mündlich erklären können solltest
- 34. Komplexes modulares Beispielprojekt: Enterprise Commerce Platform
- 34.1 Fachliches Szenario
- 34.2 Architekturübersicht
- 34.3 Projektstruktur
- 34.4 Maven Parent-POM
- 34.5 Warum gemeinsame Libraries vorsichtig verwenden?
- 34.6 Event Contracts
- 34.7 Customer Service
- 34.8 Catalog Service
- 34.9 Order Service als Orchestrator
- 34.10 Payment Service
- 34.11 Notification Service als Event Consumer
- 34.12 API Gateway
- 34.13 Lokale Infrastruktur mit Docker Compose
- 34.14 Smoke-Test-Ablauf
- 34.15 Kubernetes-Beispiel
- 34.16 Erweiterungsaufgaben
- 34.17 Lernfragen zum Beispielprojekt
- 35. Quellen und offizielle Dokumentation
#1. Orientierung: Was du am Ende können sollst
Nach diesem Lernpfad solltest du in der Lage sein:
| Fähigkeit | Spring / Spring Boot | Jakarta EE |
|---|---|---|
| Dependency Injection verstehen | @Component, @Service, @Bean, ApplicationContext |
CDI: @ApplicationScoped, @Inject, Producer, Qualifier |
| REST APIs bauen | Spring MVC: @RestController, @RequestMapping |
Jakarta REST/JAX-RS: @Path, @GET, @POST |
| Datenbankzugriff umsetzen | Spring Data JPA, JDBC, Transactions | Jakarta Persistence/JPA, JTA |
| Sicherheit einbauen | Spring Security, OAuth2 Resource Server, Method Security | Jakarta Security, Container Security, Rollen |
| Tests schreiben | Unit Tests, Slice Tests, Integrationstests | Unit Tests, Container-/Deployment-Tests |
| Enterprise Build verstehen | Maven Parent/BOM, Profiles, Plugin Management | WAR/EAR, Application Server, Maven Profiles |
| Produktivbetrieb vorbereiten | Actuator, Observability, Docker, CI/CD | App Server Deployment, Health Checks, Logs, Config |
| Architektur entscheiden | Microservice, modulare Monolithen, hexagonal | standardisierte Enterprise-Plattform, App-Server-zentriert |
Merksatz: Spring ist ein Framework-Ökosystem, das viele Entscheidungen pragmatisch vereinfacht. Jakarta EE ist eine standardisierte Plattform aus Spezifikationen und APIs, die von kompatiblen Servern implementiert werden.
#2. Lernstrategie und Reihenfolge
#2.1 Nicht alles gleichzeitig lernen
Spring und Jakarta EE überschneiden sich stark, aber sie haben unterschiedliche Denkmodelle:
- Spring: Du startest meistens mit Spring Boot, wählst Starter Dependencies und baust eine lauffähige Anwendung als JAR.
- Jakarta EE: Du entwickelst gegen Spezifikationen und deployest häufig ein WAR oder EAR auf einen kompatiblen Application Server.
Die beste Reihenfolge:
- Java, Maven, HTTP und SQL festigen.
- Dependency Injection lernen.
- REST API bauen.
- Persistenz und Transaktionen verstehen.
- Validierung und Fehlerbehandlung ergänzen.
- Security einbauen.
- Tests systematisch schreiben.
- Deployment, Monitoring und Betrieb verstehen.
- Architektur-Patterns anwenden.
#2.2 Drei Lernmodi
| Modus | Zweck | Beispiel |
|---|---|---|
| Lesen | Begriffe und Konzepte verstehen | Was ist CDI? Was ist Auto-Configuration? |
| Nachbauen | Muskelgedächtnis aufbauen | Controller, Repository, Service schreiben |
| Erklären | Wissen stabilisieren | Ein Plugin, eine Annotation oder einen Request Flow dokumentieren |
#2.3 Der wichtigste Perspektivwechsel
In Enterprise Java geht es selten nur darum, „Code zum Laufen zu bringen“. Wichtig sind:
- Wartbarkeit
- klare Modulgrenzen
- nachvollziehbare Builds
- stabile Tests
- sichere Konfiguration
- kontrollierte Releases
- gute Fehlerdiagnose
#3. Technisches Setup
#3.1 Empfohlene Werkzeuge
| Bereich | Werkzeug |
|---|---|
| JDK | JDK 17, 21 oder passend zum Ziel-Framework |
| Build | Maven 3.9+ oder kompatible Unternehmensversion |
| IDE | IntelliJ IDEA, Eclipse, VS Code mit Java-Erweiterungen |
| Datenbank lokal | PostgreSQL oder MariaDB per Docker Compose |
| API Tests | curl, HTTPie, Postman oder IntelliJ HTTP Client |
| Container | Docker oder Podman |
| Git | Feature Branch Workflow, Pull Requests |
Spring Framework 6 wechselte auf den jakarta.*-Namespace und eine Jakarta-EE-9+-Basis; Spring Framework 7 nutzt eine Jakarta-EE-11-Basis, während die Spring-Dokumentation weiterhin die Spring-Module wie Core, Testing, Data Access, Web MVC und WebFlux als zentrale Teile beschreibt.[^spring-overview][^spring-versions]
Jakarta EE stellt eine standardisierte Enterprise-Java-Plattform bereit; Jakarta EE 11 ist die aktuelle Plattformlinie und Jakarta EE 12 ist offiziell als „under development“ geführt.1[^jakarta-ee11][^jakarta-ee12]
#3.2 Maven-Grundlagen prüfen
Du solltest folgende Maven-Befehle sicher verstehen:
mvn clean test
mvn clean package
mvn clean verify
mvn clean install
mvn dependency:tree
mvn help:effective-pom
mvn help:effective-settings
Wichtige Begriffe:
| Begriff | Bedeutung |
|---|---|
pom.xml |
Projektmodell mit Dependencies, Plugins, Build-Konfiguration |
| Parent-POM | zentrale Vererbung von Versionen und Build-Regeln |
| BOM | Bill of Materials zur konsistenten Dependency-Versionierung |
| Lifecycle | Maven-Phasen wie compile, test, package, verify, install, deploy |
| Plugin | Erweiterung, die Build-Aufgaben ausführt |
| Profile | alternative Build-Konfigurationen für Umgebungen |
#4. Gemeinsame Grundlagen für Spring und Jakarta EE
#4.1 HTTP und REST
Du musst sicher erklären können:
- HTTP-Methoden:
GET,POST,PUT,PATCH,DELETE - Statuscodes:
200,201,204,400,401,403,404,409,500 - Header:
Content-Type,Accept,Authorization,Location - JSON als Standarddatenformat
- Idempotenz bei
PUTundDELETE - Unterschied zwischen Ressource, DTO und Entity
Beispiel einer sauberen REST-Ressource:
GET /api/customers
POST /api/customers
GET /api/customers/{id}
PUT /api/customers/{id}
DELETE /api/customers/{id}
#4.2 Dependency Injection
Dependency Injection bedeutet: Eine Klasse erzeugt ihre Abhängigkeiten nicht selbst, sondern bekommt sie von einem Container geliefert.
Schlecht:
class OrderService {
private final OrderRepository repository = new JdbcOrderRepository();
}
Besser:
class OrderService {
private final OrderRepository repository;
OrderService(OrderRepository repository) {
this.repository = repository;
}
}
Vorteile:
- besser testbar
- weniger Kopplung
- klare Abhängigkeiten
- austauschbare Implementierungen
#4.3 Schichtenarchitektur
Eine typische Enterprise-Struktur:
api/ REST Controller oder JAX-RS Resources
dto/ Request- und Response-Objekte
application/ Use Cases / Application Services
domain/ Entities, Value Objects, Domain Services
persistence/ Repositories, JPA, SQL, Migrations
config/ Framework-Konfiguration
security/ Authentifizierung und Autorisierung
Regel: Die Fachlogik sollte nicht in Controller oder Resource-Klassen liegen.
#4.4 Transaktionen
Transaktionen schützen fachliche Konsistenz:
Bestellung anlegen
→ Lagerbestand reduzieren
→ Zahlung reservieren
→ Ereignis speichern
Wenn Schritt 3 fehlschlägt, sollen Schritt 1 und 2 typischerweise zurückgerollt werden.
In Spring verwendest du oft:
@Transactional
public OrderId placeOrder(PlaceOrderCommand command) {
// fachlicher Ablauf
}
In Jakarta EE verwendest du je nach Umgebung containerverwaltete Transaktionen oder JTA.
#5. Spring-Lernpfad
Die Spring-Dokumentation beschreibt Spring als Familie von Projekten; das Spring Framework bildet die Grundlage und enthält unter anderem Core Container, Testing, Data Access, Spring MVC und Spring WebFlux.2
Spring Boot erleichtert das Erstellen eigenständig lauffähiger, produktionsnaher Spring-Anwendungen und setzt auf eine meinungsstarke Konfiguration mit wenig manueller Einrichtung.3
#5.1 Modul S1: Spring Core und Dependency Injection
#Lernziele
Nach diesem Modul kannst du:
- Spring Beans erklären
- Constructor Injection anwenden
- Bean Scopes unterscheiden
- Konfiguration mit
@Configurationund@Beanschreiben - Profile und Properties verwenden
- zyklische Abhängigkeiten erkennen
#Kernbegriffe
| Begriff | Erklärung |
|---|---|
| Bean | Objekt, das vom Spring Container verwaltet wird |
| ApplicationContext | zentraler Spring Container |
| Component Scan | automatisches Finden von Klassen mit Stereotyp-Annotationen |
| Constructor Injection | bevorzugte Injektion über Konstruktor |
| Profile | Aktivierung bestimmter Beans oder Konfigurationen pro Umgebung |
#Beispiel
@Service
public class InvoiceService {
private final TaxCalculator taxCalculator;
public InvoiceService(TaxCalculator taxCalculator) {
this.taxCalculator = taxCalculator;
}
public Invoice createInvoice(Money netAmount) {
Money tax = taxCalculator.calculate(netAmount);
return new Invoice(netAmount, tax);
}
}
#Übung
Baue drei Klassen:
PriceCalculatorDiscountPolicyCheckoutService
CheckoutService darf keine Implementierung selbst mit new erzeugen. Alle Abhängigkeiten müssen injiziert werden.
#Typische Fehler
- Feldinjektion statt Constructor Injection
- Geschäftslogik in Konfigurationsklassen
- zu viele Beans ohne klare Verantwortlichkeit
- zyklische Abhängigkeiten zwischen Services
#5.2 Modul S2: Spring Boot Grundlagen
#Lernziele
Du kannst danach:
- ein Spring-Boot-Projekt strukturieren
- Starter Dependencies verstehen
application.ymlverwenden- Auto-Configuration erklären
- Actuator aktivieren
- Profile für
dev,test,prodeinsetzen
#Minimaler Maven-Build
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.1.0</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>customer-service</artifactId>
<version>1.0.0-SNAPSHOT</version>
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
#Beispiel application.yml
spring:
application:
name: customer-service
profiles:
active: dev
server:
port: 8080
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
#Übung
Erstelle ein Projekt mit:
/api/helloEndpointdevundprodProfil- unterschiedlichem Server-Port pro Profil
- Actuator Health Endpoint
#5.3 Modul S3: REST APIs mit Spring MVC
#Lernziele
Du kannst:
- Controller sauber von Services trennen
- DTOs verwenden
- Bean Validation einsetzen
- Fehler zentral behandeln
- HTTP-Statuscodes bewusst setzen
#Beispiel Controller
@RestController
@RequestMapping("/api/customers")
public class CustomerController {
private final CustomerService service;
public CustomerController(CustomerService service) {
this.service = service;
}
@PostMapping
public ResponseEntity<CustomerResponse> create(@Valid @RequestBody CreateCustomerRequest request) {
CustomerResponse created = service.create(request);
URI location = URI.create("/api/customers/" + created.id());
return ResponseEntity.created(location).body(created);
}
@GetMapping("/{id}")
public CustomerResponse findById(@PathVariable UUID id) {
return service.findById(id);
}
}
#DTO mit Validation
public record CreateCustomerRequest(
@NotBlank String firstName,
@NotBlank String lastName,
@Email String email
) {}
#Zentrale Fehlerbehandlung
@RestControllerAdvice
public class ApiExceptionHandler {
@ExceptionHandler(CustomerNotFoundException.class)
ResponseEntity<ApiError> handleNotFound(CustomerNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ApiError("CUSTOMER_NOT_FOUND", ex.getMessage()));
}
}
#Übung
Baue CRUD für Product:
POST /api/productsGET /api/products/{id}GET /api/productsPUT /api/products/{id}DELETE /api/products/{id}
Achte darauf, dass Controller keine Datenbanklogik enthalten.
#5.4 Modul S4: Spring Data, JPA und Transaktionen
Spring Data JPA bietet Repository-Unterstützung für Jakarta Persistence API/JPA und vereinfacht Datenzugriff mit einem konsistenten Programmiermodell.[^spring-data-jpa]
#Lernziele
Du kannst:
- Entities und DTOs trennen
- Repositories definieren
- Query Methods verwenden
- Transaktionen an der Service-Schicht setzen
- Lazy Loading und N+1-Probleme erklären
- Migrationen mit Flyway oder Liquibase planen
#Entity
@Entity
@Table(name = "customers")
public class CustomerEntity {
@Id
private UUID id;
@Column(nullable = false)
private String firstName;
@Column(nullable = false)
private String lastName;
@Column(nullable = false, unique = true)
private String email;
protected CustomerEntity() {}
public CustomerEntity(UUID id, String firstName, String lastName, String email) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
}
#Repository
public interface CustomerRepository extends JpaRepository<CustomerEntity, UUID> {
boolean existsByEmail(String email);
Optional<CustomerEntity> findByEmail(String email);
}
#Service mit Transaktion
@Service
public class CustomerService {
private final CustomerRepository repository;
public CustomerService(CustomerRepository repository) {
this.repository = repository;
}
@Transactional
public CustomerResponse create(CreateCustomerRequest request) {
if (repository.existsByEmail(request.email())) {
throw new DuplicateCustomerEmailException(request.email());
}
CustomerEntity entity = new CustomerEntity(
UUID.randomUUID(),
request.firstName(),
request.lastName(),
request.email()
);
CustomerEntity saved = repository.save(entity);
return CustomerResponse.from(saved);
}
}
#Übung
Baue eine Datenbankmigration:
CREATE TABLE customers (
id UUID PRIMARY KEY,
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100) NOT NULL,
email VARCHAR(200) NOT NULL UNIQUE
);
Dann schreibe:
- Repository-Test
- Service-Test
- Integrationstest mit echter Datenbank
#5.5 Modul S5: Spring Security
Spring Security bietet umfassende Unterstützung für OAuth 2.0 und kann unter anderem als OAuth2 Resource Server JWT- oder opaque-token-basierte Bearer Tokens absichern.[^spring-security-oauth2][^spring-security-resource]
#Lernziele
Du kannst:
- Authentication und Authorization unterscheiden
- Security Filter Chain erklären
- Endpoints nach Rollen absichern
- JWT Resource Server konfigurieren
- Method Security einsetzen
- sichere Fehlerantworten planen
#Beispiel Security-Konfiguration
@Configuration
@EnableMethodSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain apiSecurity(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/health").permitAll()
.requestMatchers(HttpMethod.GET, "/api/products/**").hasAuthority("SCOPE_products:read")
.requestMatchers(HttpMethod.POST, "/api/products/**").hasAuthority("SCOPE_products:write")
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
.build();
}
}
#Übung
Sichere eine Produkt-API so ab:
| Aktion | Scope |
|---|---|
| Produkt lesen | products:read |
| Produkt anlegen | products:write |
| Produkt löschen | products:admin |
Schreibe Tests für 401, 403 und 200.
#5.6 Modul S6: Testing in Spring
Spring bietet im spring-test-Modul Unterstützung für Integrationstests mit Spring Container; das TestContext Framework bietet generische, annotationsgetriebene Testunterstützung.[^spring-testing][^spring-testcontext]
#Test-Pyramide
| Ebene | Ziel | Beispiel |
|---|---|---|
| Unit Test | reine Fachlogik | PriceCalculatorTest |
| Slice Test | ein Ausschnitt | @WebMvcTest, @DataJpaTest |
| Integrationstest | mehrere Schichten | @SpringBootTest |
| Systemtest | komplette App | API Test gegen laufenden Container |
#Beispiel MVC-Test
@WebMvcTest(CustomerController.class)
class CustomerControllerTest {
@Autowired
MockMvc mockMvc;
@MockBean
CustomerService customerService;
@Test
void createReturns201() throws Exception {
when(customerService.create(any())).thenReturn(new CustomerResponse(UUID.randomUUID(), "Ada", "Lovelace"));
mockMvc.perform(post("/api/customers")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{"firstName":"Ada","lastName":"Lovelace","email":"ada@example.com"}
"""))
.andExpect(status().isCreated());
}
}
#Übung
Erstelle für eine Order-API:
- Unit Test für Preisberechnung
@WebMvcTestfür Controller@DataJpaTestfür Repository@SpringBootTestfür vollständigen Request Flow
#5.7 Modul S7: Spring für Produktion
#Lernziele
Du kannst:
- Health Checks und Readiness erklären
- Actuator Endpoints sicher bereitstellen
- Logs strukturieren
- Metriken exportieren
- Container Images bauen
- Konfiguration externalisieren
#Wichtige Produktionsfragen
| Frage | Warum wichtig? |
|---|---|
| Wie erkennt Kubernetes, ob die App lebt? | Liveness und Readiness |
| Welche Version läuft gerade? | Release-Diagnose |
| Welche Metriken gibt es? | Monitoring und SLOs |
| Wo liegen Secrets? | Security und Compliance |
| Wie werden DB-Migrationen ausgeführt? | Release-Sicherheit |
#Beispiel Dockerfile
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY target/customer-service.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
#5.8 Modul S8: Spring Cloud und verteilte Systeme
Spring Cloud stellt Werkzeuge für typische Muster verteilter Systeme bereit, darunter Configuration Management, Service Discovery, Circuit Breaker und Routing.4
Spring Cloud Gateway baut auf dem Spring-Ökosystem auf und adressiert Routing sowie Querschnittsthemen wie Security, Monitoring/Metrics und Resilienz.[^spring-cloud-gateway]
#Lernziele
Du kannst:
- zentrale Konfiguration erklären
- API Gateway Patterns verstehen
- Circuit Breaker einsetzen
- Timeouts und Retries bewusst konfigurieren
- Service-to-Service-Kommunikation absichern
#Lernreihenfolge
- Warum verteilte Systeme schwierig sind
- Timeouts, Retries, Circuit Breaker
- Gateway und Routing
- zentrale Konfiguration
- Observability über Service-Grenzen hinweg
- Security mit OAuth2/OIDC
#6. Jakarta-EE-Lernpfad
Jakarta EE ist laut offizieller Dokumentation eine Sammlung von Services für Enterprise-Anwendungen auf der Java-Plattform; sie stellt Infrastruktur bereit, damit Entwickler sich stärker auf Fachlogik konzentrieren können.5
#6.1 Modul J1: Jakarta EE Plattform verstehen
#Lernziele
Du kannst:
- Spezifikation und Implementierung unterscheiden
- Application Server erklären
- Profile verstehen: Platform, Web Profile, Core Profile
jakarta.*Namespace erklären- WAR, JAR und EAR unterscheiden
#Spezifikation vs. Implementierung
| Begriff | Beispiel |
|---|---|
| Spezifikation | Jakarta REST, Jakarta Persistence, Jakarta CDI |
| API | jakarta.ws.rs-api, jakarta.persistence-api |
| Implementierung | Server oder Bibliothek, z. B. ein kompatibler Jakarta-EE-Server |
| Deployment | WAR oder EAR auf Application Server |
#Typische Projektstruktur
jakarta-customer-app/
├── pom.xml
└── src/
├── main/
│ ├── java/com/example/customer/
│ │ ├── boundary/CustomerResource.java
│ │ ├── control/CustomerService.java
│ │ └── entity/Customer.java
│ ├── resources/META-INF/persistence.xml
│ └── webapp/WEB-INF/beans.xml
└── test/java/...
#6.2 Modul J2: CDI – Contexts and Dependency Injection
#Lernziele
Du kannst:
@Injectverwenden- Scopes erklären
- Qualifier definieren
- Producer Methoden schreiben
- Interceptors einsetzen
- CDI Events verstehen
#Beispiel CDI Service
@ApplicationScoped
public class CustomerService {
@Inject
CustomerRepository repository;
public Customer create(CreateCustomerCommand command) {
Customer customer = new Customer(command.name(), command.email());
repository.persist(customer);
return customer;
}
}
#Qualifier
@Qualifier
@Retention(RUNTIME)
@Target({ FIELD, PARAMETER, METHOD, TYPE })
public @interface ExternalPaymentProvider {}
@ApplicationScoped
@ExternalPaymentProvider
public class StripePaymentClient implements PaymentClient {
public PaymentResult charge(PaymentCommand command) {
return PaymentResult.accepted();
}
}
#Übung
Baue zwei Implementierungen eines NotificationSender:
EmailNotificationSenderSmsNotificationSender
Wähle die passende Implementierung über einen CDI Qualifier aus.
#6.3 Modul J3: REST APIs mit Jakarta REST
#Lernziele
Du kannst:
- JAX-RS/Jakarta REST Resource-Klassen schreiben
- JSON Requests und Responses verarbeiten
- Statuscodes zurückgeben
- Exception Mapper verwenden
- Bean Validation integrieren
#Application-Klasse
@ApplicationPath("/api")
public class CustomerApplication extends Application {
}
#Resource-Klasse
@Path("/customers")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class CustomerResource {
@Inject
CustomerService service;
@POST
public Response create(@Valid CreateCustomerRequest request, @Context UriInfo uriInfo) {
CustomerResponse created = service.create(request);
URI location = uriInfo.getAbsolutePathBuilder()
.path(created.id().toString())
.build();
return Response.created(location).entity(created).build();
}
@GET
@Path("/{id}")
public CustomerResponse findById(@PathParam("id") UUID id) {
return service.findById(id);
}
}
#Exception Mapper
@Provider
public class CustomerNotFoundMapper implements ExceptionMapper<CustomerNotFoundException> {
@Override
public Response toResponse(CustomerNotFoundException exception) {
return Response.status(Response.Status.NOT_FOUND)
.entity(new ApiError("CUSTOMER_NOT_FOUND", exception.getMessage()))
.build();
}
}
#Übung
Baue eine Jakarta REST API für InventoryItem mit:
GET /api/itemsPOST /api/itemsGET /api/items/{id}- Exception Mapper für
404 - Validation für Pflichtfelder
#6.4 Modul J4: Jakarta Persistence und Transaktionen
Jakarta Persistence stellt Object/Relational Mapping für relationale Daten in Java-Anwendungen bereit.[^jakarta-persistence]
#Lernziele
Du kannst:
- JPA Entities modellieren
EntityManagerverwenden- JPQL schreiben
- Beziehungen modellieren
- Transaktionen erklären
persistence.xmlkonfigurieren
#Entity
@Entity
@Table(name = "customers")
public class Customer {
@Id
private UUID id;
@Column(nullable = false)
private String name;
@Column(nullable = false, unique = true)
private String email;
protected Customer() {}
public Customer(String name, String email) {
this.id = UUID.randomUUID();
this.name = name;
this.email = email;
}
}
#Repository mit EntityManager
@ApplicationScoped
public class CustomerRepository {
@PersistenceContext
EntityManager em;
public void persist(Customer customer) {
em.persist(customer);
}
public Optional<Customer> findById(UUID id) {
return Optional.ofNullable(em.find(Customer.class, id));
}
}
#persistence.xml
<persistence xmlns="https://jakarta.ee/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_2.xsd"
version="3.2">
<persistence-unit name="customerPU" transaction-type="JTA">
<jta-data-source>java:/jdbc/customerDb</jta-data-source>
<properties>
<property name="jakarta.persistence.schema-generation.database.action" value="none"/>
</properties>
</persistence-unit>
</persistence>
#Übung
Erweitere Customer um Address:
@Embeddedoder eigene Entity?- Welche Entscheidung passt besser?
- Was bedeutet das für Queries?
- Wie testest du die Persistenz?
#6.5 Modul J5: Jakarta Security
#Lernziele
Du kannst:
- Container Security erklären
- Rollen und Berechtigungen unterscheiden
- sichere Endpoints definieren
- Authentifizierungsmechanismen verstehen
- Security-Konfiguration serverseitig einordnen
#Beispiel Rollenprüfung
@Path("/admin/reports")
@RolesAllowed("ADMIN")
public class AdminReportResource {
@GET
public ReportOverview overview() {
return new ReportOverview("ok");
}
}
#Übung
Sichere Endpoints so ab:
| Endpoint | Rolle |
|---|---|
/api/public |
keine Authentifizierung |
/api/customers |
USER |
/api/admin |
ADMIN |
Dokumentiere, was bei nicht authentifizierten und nicht autorisierten Requests passieren soll.
#6.6 Modul J6: Batch, Messaging und Enterprise Integration
#Lernziele
Du kannst:
- Batch-Verarbeitung von Request/Response unterscheiden
- Jobs, Steps und Chunk-Verarbeitung erklären
- Messaging-Grundbegriffe verstehen
- asynchrone Verarbeitung fachlich begründen
#Wann Batch?
Batch passt, wenn:
- große Datenmengen regelmäßig verarbeitet werden
- Import/Export-Prozesse laufen
- Fehler wiederholbar behandelt werden müssen
- Verarbeitung auditierbar sein muss
Beispiele:
- nächtlicher Kundendatenimport
- Rechnungsbatch
- CSV-Export
- Datenbereinigung
#6.7 Modul J7: Jakarta Deployment und Application Server
#Lernziele
Du kannst:
- WAR Deployment erklären
- Server-Konfiguration von Anwendungscode trennen
- Datasources serverseitig konfigurieren
- Logs und Health Checks planen
- Migration von Java EE zu Jakarta EE einschätzen
#Maven WAR Beispiel
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>jakarta-customer-app</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<maven.compiler.release>21</maven.compiler.release>
<jakartaee.version>11.0.0</jakartaee.version>
</properties>
<dependencies>
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<version>${jakartaee.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
Wichtig: provided bedeutet, dass die API zum Kompilieren verfügbar ist, aber zur Laufzeit vom Application Server bereitgestellt wird.
#7. Vergleich: Spring vs. Jakarta EE
#7.1 Denkmodell
| Thema | Spring / Spring Boot | Jakarta EE |
|---|---|---|
| Primärer Stil | Framework-Ökosystem | Standardisierte Plattform |
| Laufzeit | meistens ausführbares JAR | häufig WAR/EAR auf Server |
| Konfiguration | Auto-Configuration, Starter | Server stellt viele Dienste bereit |
| Portabilität | stark innerhalb Spring-Ökosystem | API-Portabilität zwischen Implementierungen |
| Einstieg | sehr schnell mit Boot | stärker Plattform- und Serververständnis nötig |
| Enterprise-Betrieb | App bringt viel selbst mit | Server übernimmt viele Dienste |
| Typische Teams | Microservices, Cloud-native | klassische Enterprise-Systeme, standardisierte Plattformen |
#7.2 Wann Spring?
Spring passt besonders gut, wenn:
- du schnell produktionsfähige Services bauen willst
- Microservices oder modulare Monolithen geplant sind
- ein Team hohe Kontrolle über Abhängigkeiten und Runtime haben will
- Cloud Deployment, Container und CI/CD im Fokus stehen
- Spring Security, Spring Data und Spring Cloud genutzt werden sollen
#7.3 Wann Jakarta EE?
Jakarta EE passt besonders gut, wenn:
- du gegen Standards entwickeln willst
- ein Unternehmen Application Server standardisiert betreibt
- bestehende Java-EE/Jakarta-EE-Systeme erweitert werden
- WAR/EAR-Deployment gesetzt ist
- API-Portabilität wichtiger ist als Framework-Komfort
#7.4 Mischwelt: Spring und Jakarta
Moderne Spring-Versionen verwenden ebenfalls viele Jakarta APIs, zum Beispiel Servlet, JPA und Bean Validation über jakarta.*. Darum ist Jakarta-Wissen auch für Spring-Entwickler wertvoll.
Beispiel:
import jakarta.persistence.Entity;
import jakarta.validation.constraints.NotBlank;
Der Unterschied liegt oft nicht in den APIs selbst, sondern darin, wer die Runtime und Integration bereitstellt:
- Bei Spring Boot bringt die Anwendung sehr viel über Dependencies und Auto-Configuration mit.
- Bei Jakarta EE stellt der Application Server viele Dienste bereit.
#8. Enterprise-Architektur-Lernpfad
#8.1 Modul A1: Architekturformen
#Modularer Monolith
Ein modularer Monolith ist eine Anwendung mit klaren internen Modulen, aber einem gemeinsamen Deployment.
Vorteile:
- einfacheres Deployment
- geringere Netzwerkkomplexität
- gute Transaktionskontrolle
- leichteres Debugging
Risiken:
- Module verschwimmen
- Datenbank wird zum Kopplungspunkt
- Build kann langsam werden
#Microservices
Microservices sind unabhängig deploybare Services mit klaren fachlichen Grenzen.
Vorteile:
- unabhängige Releases
- Team-Autonomie
- Skalierung pro Service
Risiken:
- verteilte Transaktionen
- Netzwerkfehler
- Observability-Aufwand
- API-Versionierung
#8.2 Modul A2: Hexagonale Architektur
Eine robuste Struktur:
domain/
model/
service/
application/
usecase/
adapter/
in/web/
out/persistence/
out/messaging/
config/
Regel:
- Domain kennt kein Spring und kein Jakarta.
- Adapter kennen Frameworks.
- Use Cases orchestrieren fachliche Abläufe.
#8.3 Modul A3: Fehlerbehandlung
Eine Enterprise API braucht konsistente Fehlerantworten:
{
"code": "CUSTOMER_NOT_FOUND",
"message": "Customer was not found",
"correlationId": "f3bd4b2e-0ad6-4bbd-a6a1-1d9498e2d1ea"
}
Lernaufgabe:
- Definiere 10 fachliche Fehlercodes.
- Ordne jedem Fehler einen HTTP-Status zu.
- Dokumentiere, welche Fehler geloggt werden.
#8.4 Modul A4: Security-Architektur
Wichtige Fragen:
- Wer ist der Identity Provider?
- Nutzt ihr OIDC/OAuth2?
- Welche Services validieren Tokens?
- Gibt es Service-to-Service Tokens?
- Werden Rollen oder Scopes verwendet?
- Wie werden Admin-Rechte auditiert?
#8.5 Modul A5: Build, Release und CI/CD
Eine Enterprise Pipeline sollte mindestens enthalten:
validate
compile
unit tests
static analysis
integration tests
package
container build
security scan
deploy to test
smoke test
promote to stage/prod
Maven-Phasen:
mvn clean verify
Für Releases:
mvn -Pproduction clean package
#9. Praxisprojekte
#9.1 Projekt 1: Spring Customer Service
#Ziel
Baue einen produktionsnahen Spring Boot Service für Kundenverwaltung.
#Funktionen
- Kunde anlegen
- Kunde lesen
- Kunde suchen
- Kunde deaktivieren
- Validierung
- Fehlerbehandlung
- Datenbankmigration
- Security mit JWT
- Actuator Health
- Integrationstests
#Akzeptanzkriterien
mvn clean verifyläuft grün.- Kein Controller enthält Datenbanklogik.
- Alle öffentlichen Fehler haben einen Fehlercode.
POST /api/customersgibt201 Createdzurück.GET /actuator/healthist ohne Auth erreichbar.- Schreiboperationen benötigen einen Write-Scope.
#9.2 Projekt 2: Jakarta Inventory Application
#Ziel
Baue eine Jakarta EE WAR-Anwendung für Lagerverwaltung.
#Funktionen
- Artikel anlegen
- Bestand erhöhen
- Bestand reduzieren
- Mindestbestand prüfen
- REST API mit Jakarta REST
- CDI Services
- JPA Persistenz
- rollenbasierte Admin-Endpunkte
#Akzeptanzkriterien
- Anwendung wird als WAR gebaut.
- Jakarta EE API ist
provided. - REST Resource enthält keine Persistenzlogik.
- Transaktionen werden bewusst dokumentiert.
- Fehler werden über Exception Mapper vereinheitlicht.
#9.3 Projekt 3: Migration Java EE / javax zu Jakarta / jakarta
#Ziel
Migriere eine kleine Anwendung von javax.* nach jakarta.*.
#Checkliste
- Imports ersetzen
- Dependencies aktualisieren
- Application Server prüfen
- Persistence-Konfiguration prüfen
- Servlet-Version prüfen
- Tests ausführen
- Deployment neu testen
Beispiel:
// alt
import javax.persistence.Entity;
import javax.validation.constraints.NotBlank;
// neu
import jakarta.persistence.Entity;
import jakarta.validation.constraints.NotBlank;
#9.4 Projekt 4: Architekturvergleich
Baue denselben Use Case zweimal:
- mit Spring Boot
- mit Jakarta EE
Use Case: Bestellung anlegen.
Vergleiche danach:
- Codeumfang
- Konfiguration
- Testbarkeit
- Deployment
- Fehlerbehandlung
- Security
- Team-Verständlichkeit
#10. Prüfungsfragen und Selbstkontrolle
#10.1 Spring Fragen
- Was ist der Unterschied zwischen
@Component,@Serviceund@Repository? - Warum ist Constructor Injection meistens besser als Field Injection?
- Was macht Spring Boot Auto-Configuration?
- Was ist ein Starter?
- Was ist der Unterschied zwischen
@WebMvcTestund@SpringBootTest? - Warum sollte
@Transactionaloft auf Service-Methoden liegen? - Was ist der Unterschied zwischen Authentication und Authorization?
- Wann entsteht ein N+1-Problem?
- Was ist ein Actuator Health Endpoint?
- Wie würdest du Secrets in Produktion bereitstellen?
#10.2 Jakarta EE Fragen
- Was ist der Unterschied zwischen Spezifikation und Implementierung?
- Warum ist die Jakarta EE API oft
provided? - Was macht CDI?
- Was ist ein Qualifier?
- Was ist eine JAX-RS Resource?
- Was ist ein Exception Mapper?
- Was ist der Zweck von
persistence.xml? - Wie unterscheidet sich WAR Deployment von executable JAR Deployment?
- Was bedeutet containerverwaltete Transaktion?
- Was musst du bei Migration von
javaxzujakartaprüfen?
#10.3 Architekturfragen
- Wann ist ein modularer Monolith besser als Microservices?
- Warum sollte Domain-Code frameworkfrei sein?
- Wie entwirfst du eine stabile Fehlerantwort?
- Was gehört in eine CI/CD Pipeline?
- Welche Metriken braucht eine produktive API?
- Wie versionierst du APIs?
- Was ist ein Correlation ID?
- Warum sind Timeouts Pflicht?
- Was sind idempotente Operationen?
- Wie erkennst du zu starke Kopplung zwischen Modulen?
#11. Empfohlene Reihenfolge als 16-Wochen-Plan
| Woche | Fokus | Ergebnis |
|---|---|---|
| 1 | Maven, HTTP, SQL auffrischen | kleine CLI-App + Maven Build |
| 2 | Dependency Injection allgemein | DI ohne Framework erklären können |
| 3 | Spring Core | Beans, Profiles, Properties |
| 4 | Spring Boot Grundlagen | lauffähige REST App |
| 5 | Spring MVC + Validation | saubere CRUD API |
| 6 | Spring Data JPA | Datenbankanbindung + Migration |
| 7 | Spring Testing | Testpyramide umsetzen |
| 8 | Spring Security | JWT Resource Server absichern |
| 9 | Spring Produktion | Actuator, Logs, Docker |
| 10 | Jakarta EE Plattform | WAR, Server, APIs, provided |
| 11 | CDI | Injection, Scopes, Qualifier |
| 12 | Jakarta REST | JAX-RS API mit Fehlerbehandlung |
| 13 | Jakarta Persistence | EntityManager, JPA, Transaktionen |
| 14 | Jakarta Security + Deployment | Rollen, Server-Konfiguration |
| 15 | Architektur | hexagonal, modularer Monolith, Microservices |
| 16 | Abschlussprojekt | Spring und Jakarta Projekt vergleichen |
#12. Referenz-Templates
#12.1 Spring Projektstruktur
src/main/java/com/example/customer/
├── CustomerApplication.java
├── api/
│ ├── CustomerController.java
│ ├── CreateCustomerRequest.java
│ ├── CustomerResponse.java
│ └── ApiExceptionHandler.java
├── application/
│ └── CustomerService.java
├── domain/
│ ├── Customer.java
│ └── CustomerEmail.java
├── persistence/
│ ├── CustomerEntity.java
│ └── CustomerRepository.java
├── security/
│ └── SecurityConfig.java
└── config/
└── JacksonConfig.java
#12.2 Jakarta EE Projektstruktur
src/main/java/com/example/inventory/
├── boundary/
│ ├── InventoryApplication.java
│ ├── InventoryResource.java
│ └── InventoryExceptionMapper.java
├── control/
│ └── InventoryService.java
├── entity/
│ ├── InventoryItem.java
│ └── StockMovement.java
└── security/
└── Roles.java
src/main/resources/META-INF/persistence.xml
src/main/webapp/WEB-INF/beans.xml
#12.3 Maven Quality Parent Beispiel
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.14.0</version>
<configuration>
<release>${java.version}</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.3</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.5.3</version>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.12</version>
</plugin>
</plugins>
</pluginManagement>
</build>
#12.4 Docker Compose für lokale Datenbank
services:
postgres:
image: postgres:16
environment:
POSTGRES_DB: enterprise
POSTGRES_USER: enterprise
POSTGRES_PASSWORD: enterprise
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U enterprise"]
interval: 10s
timeout: 5s
retries: 5
#12.5 Lernnotiz-Template
# Thema
## Was ist es?
## Warum braucht man es?
## Minimalbeispiel
## Typische Fehler
## Enterprise-Relevanz
## Prüfungsfrage
#13. Erweiterter Framework-Kompass
Dieser Abschnitt erweitert den ursprünglichen Spring/Jakarta-Lernpfad um Frameworks, Tools und Plattformen, die in Enterprise-Java-Projekten häufig neben oder anstelle von Spring und Jakarta EE auftauchen.
#13.1 Die wichtigsten Ökosysteme auf einen Blick
| Bereich | Typische Technologie | Warum du sie lernen solltest |
|---|---|---|
| Spring-Ökosystem | Spring Boot, Spring MVC, Spring Data, Spring Security, Spring Cloud | Sehr verbreitet, produktiv, riesiges Ökosystem |
| Jakarta-Ökosystem | Jakarta EE, CDI, Jakarta REST, Jakarta Persistence, Jakarta Security | Standardisierte Enterprise-Java-APIs, Application Server, langfristige Portabilität |
| MicroProfile | Config, Health, OpenAPI, Fault Tolerance, REST Client, Telemetry | Standardisierte Microservice-Erweiterungen für Jakarta-nahe Runtimes |
| Cloud-native Java | Quarkus, Micronaut, Helidon | Startzeit, geringer Speicherverbrauch, Kubernetes, Native Image, Microservices |
| Persistenz | JPA, Hibernate, Spring Data, jOOQ, Flyway, Liquibase | Fast jedes Enterprise-System braucht robuste Datenhaltung und Migrationen |
| Messaging | Kafka, RabbitMQ, JMS, Spring Cloud Stream, Apache Camel | Entkopplung, Events, Integration, asynchrone Prozesse |
| Security | Spring Security, Jakarta Security, OAuth2, OIDC, Keycloak | Authentifizierung, Autorisierung, Token, Rollen, Zero Trust |
| Observability | Actuator, Micrometer, OpenTelemetry, Prometheus, Grafana, Loki | Produktivbetrieb verstehen, Fehler analysieren, SLAs überwachen |
| Testing | JUnit, Mockito, AssertJ, Testcontainers, WireMock, Pact, ArchUnit | Qualität, Refactoring-Sicherheit, echte Integrationstests |
| Deployment | Docker, Kubernetes, Helm, Kustomize, GitOps | Reproduzierbare Releases, Skalierung, Cloud-Betrieb |
#13.2 Lernreihenfolge nach Rolle
| Rolle | Fokus der ersten 8 Wochen | Fokus danach |
|---|---|---|
| Backend-Entwickler | Spring Boot, REST, JPA, Testing | Security, Messaging, Observability |
| Jakarta-EE-Entwickler | CDI, Jakarta REST, JPA, App Server | MicroProfile, Cloud Deployment, Migration zu jakarta.* |
| Cloud-native-Entwickler | Docker, Kubernetes, Quarkus/Micronaut | Native Image, Observability, Event-driven Architektur |
| Softwarearchitekt | Architekturpatterns, Modulgrenzen, Datenkonsistenz | Plattformstrategie, Migration, Governance, Qualitätsmetriken |
| DevOps/Platform Engineer | Build, Container, Kubernetes, CI/CD | Observability, Security Scanning, Release-Automatisierung |
#13.3 Lernregel: Frameworks nicht isoliert lernen
Ein Framework wird erst wirklich verständlich, wenn du dieselbe Fachfunktion mehrfach umsetzt:
CustomerControllerals Spring MVC REST API.CustomerResourceals Jakarta REST API.- dieselbe API mit Quarkus REST.
- dieselbe API mit Micronaut Controller.
- dieselbe API mit Tests, Datenbank, Security und Docker.
Dadurch lernst du nicht nur Syntax, sondern Framework-Denken, Konfiguration, Build, Testing und Betrieb.
#14. MicroProfile-Lernpfad
MicroProfile ergänzt Enterprise Java um Spezifikationen, die besonders für Microservices wichtig sind. Es ist kein einzelnes Framework wie Spring Boot, sondern ein Spezifikationsbündel, das von Runtimes wie Open Liberty, Payara, Quarkus oder Helidon MP implementiert werden kann.
#14.1 Was du zuerst verstehen solltest
| Thema | Zweck |
|---|---|
| CDI als Basis | Dependency Injection und Bean-Lifecycle |
| Jakarta REST | REST-Endpunkte als Grundlage vieler MicroProfile-Spezifikationen |
| MicroProfile Config | Externe Konfiguration aus Properties, Umgebungsvariablen oder anderen Quellen |
| MicroProfile Health | Readiness- und Liveness-Prüfungen für Plattformen wie Kubernetes |
| MicroProfile OpenAPI | Automatische API-Dokumentation für REST-Services |
| MicroProfile Rest Client | Typsichere REST-Clients |
| MicroProfile Fault Tolerance | Timeout, Retry, Circuit Breaker, Bulkhead, Fallback |
| MicroProfile Telemetry | Tracing, Metriken und Logs mit OpenTelemetry-Konzepten |
#14.2 Minimalbeispiel: Config + REST
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import org.eclipse.microprofile.config.inject.ConfigProperty;
@Path("/hello")
public class HelloResource {
@ConfigProperty(name = "app.greeting", defaultValue = "Hallo")
String greeting;
@GET
public String hello() {
return greeting + " Enterprise Java";
}
}
#14.3 Minimalbeispiel: Health Check
import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.Readiness;
import jakarta.enterprise.context.ApplicationScoped;
@Readiness
@ApplicationScoped
public class DatabaseReadinessCheck implements HealthCheck {
@Override
public HealthCheckResponse call() {
boolean databaseAvailable = true;
return HealthCheckResponse.named("database")
.status(databaseAvailable)
.build();
}
}
#14.4 Übung
Baue einen kleinen order-service mit diesen Anforderungen:
- REST-Endpunkt
POST /orders - Konfiguration
order.max-items - Health-Check für Datenbankverbindung
- OpenAPI-Dokumentation
- Rest Client für einen fiktiven
customer-service - Fault-Tolerance-Regel: Bei Timeout soll ein Fallback ausgeführt werden
#14.5 Typische Fehler
| Fehler | Folge | Bessere Lösung |
|---|---|---|
| Alles als MicroProfile lernen, ohne CDI verstanden zu haben | Annotationen bleiben Magie | Erst CDI, Scope, Producer und Qualifier lernen |
| Health Checks mit Business-Checks überladen | Kubernetes startet/killt Pods falsch | Readiness und Liveness klar trennen |
| Retry ohne Idempotenz | doppelte Bestellungen, doppelte Zahlungen | Retry nur bei sicheren Operationen oder mit Idempotency Key |
| Config-Werte fest in Code schreiben | Umgebung schwer änderbar | MicroProfile Config oder externe Konfigurationsquelle nutzen |
#15. Quarkus-Lernpfad
Quarkus ist ein modernes Cloud-native-Java-Framework. Es kombiniert Jakarta- und MicroProfile-nahe APIs mit Build-Time-Optimierung, schnellem Dev-Mode, Kubernetes-Fokus und guter Native-Image-Unterstützung.
#15.1 Wann Quarkus besonders interessant ist
| Situation | Warum Quarkus passt |
|---|---|
| Kubernetes-first Microservices | gute Kubernetes- und Container-Unterstützung |
| kurze Startzeit wichtig | Build-Time-Verarbeitung und Native-Image-Option |
| geringer Speicherverbrauch wichtig | optimiertes Runtime-Modell |
| Team kennt Jakarta/CDI | viele Konzepte fühlen sich vertraut an |
| Event-driven Services | Erweiterungen für Kafka, Messaging, REST Clients |
#15.2 Quarkus-Grundlagen
Lerne in dieser Reihenfolge:
- Projektstruktur und Dev Mode.
- REST-Endpunkte.
- CDI Beans und Injection.
- Konfiguration.
- Hibernate ORM mit Panache oder klassischem Repository-Stil.
- Tests mit Quarkus Test.
- Container Image.
- Kubernetes Deployment.
- Native Image.
- Observability und Health.
#15.3 Minimalbeispiel: REST Resource
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/customers")
public class CustomerResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
public List<CustomerDto> list() {
return List.of(new CustomerDto(1L, "Ada Lovelace"));
}
}
#15.4 Quarkus mit Panache
import io.quarkus.hibernate.orm.panache.PanacheEntity;
import jakarta.persistence.Entity;
@Entity
public class Customer extends PanacheEntity {
public String name;
public String email;
}
@Path("/customers")
public class CustomerResource {
@GET
public List<Customer> all() {
return Customer.listAll();
}
}
#15.5 Übung
Implementiere denselben Customer Service einmal mit Spring Boot und einmal mit Quarkus. Vergleiche:
- Projektstart
- Konfiguration
- REST-Annotationen
- Testaufbau
- Container Image
- Startzeit lokal
- Speicherverbrauch grob mit
docker stats
#15.6 Quarkus-Merksätze
- Quarkus verschiebt viel Arbeit in die Build-Zeit.
- Extensions sind zentral. Man fügt nicht nur Libraries hinzu, sondern Quarkus-Erweiterungen.
- Native Image ist mächtig, aber nicht automatisch immer die beste Wahl.
- Quarkus fühlt sich für Jakarta- und MicroProfile-Entwickler natürlicher an als für reine Spring-Entwickler.
#16. Micronaut-Lernpfad
Micronaut ist ein modernes JVM-Framework für modulare, testbare Microservices und serverless Anwendungen. Ein Kernmerkmal ist die starke Compile-Time-Verarbeitung: Dependency Injection, AOP-ähnliche Features und Konfiguration werden möglichst früh analysiert.
#16.1 Warum Micronaut lernen?
| Stärke | Bedeutung |
|---|---|
| Compile-Time DI | weniger Reflection, schnelle Starts |
| Cloud-native Fokus | gut für Microservices und serverless Szenarien |
| Multi-Language JVM | Java, Kotlin und Groovy |
| Testbarkeit | Framework ist stark auf testbare Komponenten ausgelegt |
| HTTP Client/Server | gute Unterstützung für deklarative Clients und APIs |
#16.2 Minimalbeispiel: Controller
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
@Controller("/hello")
public class HelloController {
@Get
public String hello() {
return "Hallo Micronaut";
}
}
#16.3 Bean und Konfiguration
import io.micronaut.context.annotation.Value;
import jakarta.inject.Singleton;
@Singleton
public class GreetingService {
private final String greeting;
public GreetingService(@Value("${app.greeting:Hallo}") String greeting) {
this.greeting = greeting;
}
public String greet(String name) {
return greeting + " " + name;
}
}
#16.4 Lernprojekt
Baue einen notification-service:
- REST API für Benachrichtigungen
- deklarativer HTTP Client zu einem
user-service - Konfiguration pro Umgebung
- Tests mit eingebettetem Server
- Docker Image
- optional: Native Image
#16.5 Vergleich zu Spring Boot
| Frage | Spring Boot | Micronaut |
|---|---|---|
| Lernkurve | sehr viele Tutorials, großer Markt | kleineres, modernes Ökosystem |
| DI-Modell | Runtime + AOT-Unterstützung | stark Compile-Time-orientiert |
| Auto-Konfiguration | sehr umfassend | ebenfalls stark, aber anders strukturiert |
| Enterprise-Adoption | sehr hoch | selektiv, oft bei Cloud-native/Serverless Teams |
#17. Helidon-Lernpfad
Helidon ist ein Java-Microservices-Framework mit zwei Denkmodellen:
- Helidon SE: funktionaler, expliziter, leichtgewichtiger Stil.
- Helidon MP: MicroProfile-Stil mit CDI, Jakarta REST und bekannten Enterprise-Annotationen.
#17.1 Wann Helidon lernen?
Helidon lohnt sich besonders, wenn du Enterprise Java mit MicroProfile verbinden willst oder wenn du bewusst eine Alternative zu Spring Boot und Quarkus evaluierst.
#17.2 Helidon MP Minimalbeispiel
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@Path("/ping")
@ApplicationScoped
public class PingResource {
@GET
public String ping() {
return "pong";
}
}
#17.3 Helidon SE Denkweise
Bei Helidon SE konfigurierst du vieles expliziter. Das ist weniger „magisch“, verlangt aber mehr bewusstes Design.
import io.helidon.webserver.WebServer;
import io.helidon.webserver.http.HttpRouting;
public class Main {
public static void main(String[] args) {
WebServer.builder()
.routing(Main::routing)
.port(8080)
.build()
.start();
}
static void routing(HttpRouting.Builder routing) {
routing.get("/hello", (req, res) -> res.send("Hallo Helidon"));
}
}
#17.4 Übung
Setze denselben Health-Endpunkt in drei Varianten um:
- Spring Boot Actuator.
- MicroProfile Health mit Helidon MP.
- expliziter Helidon-SE-Endpunkt.
Dokumentiere danach: Was ist am schnellsten geschrieben? Was ist am transparentesten? Was ist am besten standardisiert?
#18. Persistenz-Vertiefung: JPA, Hibernate, Spring Data, jOOQ, Flyway, Liquibase
Persistenz ist in Enterprise Java oft der schwierigste Teil, nicht wegen der Syntax, sondern wegen Datenmodell, Transaktionen, Performance und Migration.
#18.1 Lernreihenfolge Persistenz
- SQL wiederholen: Joins, Indexe, Constraints, Transaktionen.
- JDBC verstehen: Connection, PreparedStatement, ResultSet.
- JPA-Grundlagen: Entity, Persistence Context, Dirty Checking.
- Hibernate-Verhalten: Lazy Loading, Fetch Joins, N+1-Problem.
- Spring Data Repositories oder Jakarta Persistence DAO.
- Migrationen mit Flyway oder Liquibase.
- Performance: Indexe, Batching, Pagination, Query-Plan.
- Datenkonsistenz in verteilten Systemen: Outbox, Saga, Idempotenz.
#18.2 JPA Entity
import jakarta.persistence.*;
import java.time.Instant;
@Entity
@Table(name = "orders")
public class OrderEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String customerId;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private OrderStatus status;
@Version
private long version;
private Instant createdAt;
}
#18.3 Spring Data Repository
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface OrderRepository extends JpaRepository<OrderEntity, Long> {
List<OrderEntity> findByCustomerId(String customerId);
}
#18.4 Klassisches Jakarta DAO
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import java.util.List;
@ApplicationScoped
public class OrderDao {
@PersistenceContext
EntityManager em;
public List<OrderEntity> findByCustomerId(String customerId) {
return em.createQuery("select o from OrderEntity o where o.customerId = :customerId", OrderEntity.class)
.setParameter("customerId", customerId)
.getResultList();
}
}
#18.5 Flyway vs. Liquibase
| Frage | Flyway | Liquibase |
|---|---|---|
| Denkmodell | versionierte Migrationen, oft SQL-first | Changesets, XML/YAML/JSON/SQL möglich |
| Einfachheit | sehr einfach und konventionsorientiert | mächtiger, mehr Struktur und Features |
| Enterprise Governance | gut, aber oft über Konventionen | stark bei Auditing, Rollback-Konzepten, Policies |
| Lernaufwand | niedriger | höher |
#18.6 Flyway-Beispiel
-- src/main/resources/db/migration/V1__create_orders.sql
create table orders (
id bigserial primary key,
customer_id varchar(100) not null,
status varchar(40) not null,
version bigint not null default 0,
created_at timestamp not null
);
#18.7 Liquibase-Beispiel
databaseChangeLog:
- changeSet:
id: 001-create-orders
author: enterprise-team
changes:
- createTable:
tableName: orders
columns:
- column:
name: id
type: bigint
autoIncrement: true
constraints:
primaryKey: true
- column:
name: customer_id
type: varchar(100)
constraints:
nullable: false
#18.8 Typische Persistenzfehler
| Fehler | Symptom | Lösung |
|---|---|---|
| Entity direkt als REST DTO ausgeben | Lazy-Loading-Fehler, Datenlecks | DTOs verwenden |
Kein @Version |
verlorene Updates | Optimistic Locking einsetzen |
| Migrationen manuell in DB ausführen | Umgebungen driften auseinander | Migrationen versionieren und automatisieren |
| Unbegrenzte Queries | Speicherprobleme | Pagination und Limits |
| N+1 ignorieren | schlechte Performance | Fetch Join, EntityGraph, Query-Optimierung |
#19. API-Design: REST, OpenAPI, GraphQL, gRPC und Async APIs
Enterprise APIs sind Verträge. Ein Controller ist nur die technische Oberfläche. Wichtig sind Versionierung, Fehlerformat, Security, Idempotenz, Dokumentation und Kompatibilität.
#19.1 REST API Design Checkliste
- Ressourcen statt Verben:
/ordersstatt/createOrder. - HTTP-Methoden korrekt nutzen:
GET,POST,PUT,PATCH,DELETE. - Statuscodes bewusst verwenden.
- Fehler standardisieren, zum Beispiel Problem Details.
- Pagination, Sortierung und Filterung einheitlich gestalten.
- Idempotency Keys für kritische POST-Operationen.
- API-Versionierung planen.
- OpenAPI-Spezifikation erzeugen und prüfen.
#19.2 Spring REST Fehlerformat
import org.springframework.http.ProblemDetail;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
class ApiExceptionHandler {
@ExceptionHandler(OrderNotFoundException.class)
ProblemDetail notFound(OrderNotFoundException ex) {
ProblemDetail problem = ProblemDetail.forStatus(404);
problem.setTitle("Order not found");
problem.setDetail(ex.getMessage());
return problem;
}
}
#19.3 Jakarta REST ExceptionMapper
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
@Provider
public class OrderNotFoundMapper implements ExceptionMapper<OrderNotFoundException> {
@Override
public Response toResponse(OrderNotFoundException exception) {
return Response.status(Response.Status.NOT_FOUND)
.entity(new ErrorDto("ORDER_NOT_FOUND", exception.getMessage()))
.build();
}
}
#19.4 GraphQL lernen, wenn ...
GraphQL ist sinnvoll, wenn Clients sehr flexible Datenabfragen brauchen und du mehrere REST-Aufrufe bündeln willst. Es ist weniger sinnvoll, wenn dein Hauptproblem einfache CRUD-APIs sind.
Lernziele:
- Schema-Design
- Query, Mutation, Subscription
- Resolver
- N+1 bei GraphQL
- Security pro Feld und Operation
- Persisted Queries
#19.5 gRPC lernen, wenn ...
gRPC eignet sich für interne Service-zu-Service-Kommunikation, hohe Performance und stark typisierte Verträge. Es ist nicht automatisch besser als REST, aber sehr stark bei internen Plattformen.
Lernziele:
- Protocol Buffers
- unary und streaming calls
- Deadline/Timeout
- Backward Compatibility
- Fehlercodes
- Observability
#20. Security-Lernpfad: Spring Security, Jakarta Security, OAuth2, OIDC, Keycloak
Security ist ein Querschnittsthema. Es reicht nicht, eine Login-Seite zu bauen. Du musst Authentifizierung, Autorisierung, Token, Session, CSRF, CORS, Secrets und Auditierung verstehen.
#20.1 Grundbegriffe
| Begriff | Bedeutung |
|---|---|
| Authentication | Wer bist du? |
| Authorization | Was darfst du? |
| Principal | Identität des Benutzers oder Systems |
| Role | grobe Berechtigung, z. B. ADMIN |
| Scope | OAuth2-Berechtigungsumfang, z. B. orders:read |
| Claim | Information im Token, z. B. sub, email, roles |
| OIDC | Identity-Schicht auf OAuth2 |
| Resource Server | API, die Access Tokens prüft |
#20.2 Spring Security Resource Server Beispiel
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
class SecurityConfig {
@Bean
SecurityFilterChain apiSecurity(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/health").permitAll()
.requestMatchers("/orders/**").hasAuthority("SCOPE_orders:read")
.anyRequest().authenticated())
.oauth2ResourceServer(oauth2 -> oauth2.jwt())
.build();
}
}
#20.3 Jakarta Security Denkmodell
Jakarta Security ist stärker an standardisierte Security-Kontexte, Container und Rollenmodelle gebunden. Je nach Runtime konfigurierst du Identity Stores, Rollen und Security-Constraints.
import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@Path("/admin")
public class AdminResource {
@GET
@RolesAllowed("ADMIN")
public String adminOnly() {
return "secret";
}
}
#20.4 Keycloak-Lernprojekt
Baue eine lokale Umgebung:
- Keycloak per Docker Compose.
- Realm
enterprise. - Client
order-service. - Rollen
USER,ADMIN. - Spring Boot Resource Server.
- Jakarta- oder Quarkus-Service mit JWT-Prüfung.
- Integrationstest mit Testcontainers oder WireMock für Token-Szenarien.
#20.5 Security-Checkliste für Enterprise-Projekte
- Keine Secrets im Git.
- Token-Lebensdauer bewusst setzen.
- Refresh Token nicht im Browser Local Storage ablegen.
- CORS nicht mit
*und Credentials kombinieren. - CSRF bei Cookie-basierten Sessions beachten.
- Methode und URL absichern.
- Audit-Events schreiben.
- Security Tests automatisieren.
- Dependency-Scanning in CI.
- Rollen nicht mit Geschäftsregeln verwechseln.
#21. Messaging und Integration: Kafka, RabbitMQ, JMS, Spring Integration, Apache Camel
Messaging ist wichtig, wenn Systeme entkoppelt werden sollen. Es bringt aber neue Probleme: Reihenfolge, Zustellung, Idempotenz, Dead Letter Queues, Monitoring und Schema-Evolution.
#21.1 Kafka vs. RabbitMQ vs. JMS
| Technologie | Typischer Einsatz | Denkmodell |
|---|---|---|
| Kafka | Event Streaming, Event Logs, Analytics, Integration großer Datenströme | Topics, Partitionen, Consumer Groups, Offset |
| RabbitMQ | klassische Message Queues, Routing, Work Queues | Exchanges, Queues, Bindings, Acknowledgements |
| JMS | standardisierte Java-Messaging-API, oft im Jakarta/App-Server-Umfeld | Queue, Topic, MessageListener |
| Spring Integration | Enterprise Integration Patterns im Spring-Stil | Channels, Gateways, Transformer, Router |
| Apache Camel | umfangreiche Integrationsrouten und Konnektoren | Routes, Components, EIPs |
#21.2 Spring Kafka Consumer Beispiel
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;
@Component
public class OrderEventConsumer {
@KafkaListener(topics = "orders", groupId = "billing-service")
public void onOrderCreated(OrderCreatedEvent event) {
// idempotent verarbeiten
}
}
#21.3 RabbitMQ Denkbeispiel
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class PaymentMessageConsumer {
@RabbitListener(queues = "payment.requests")
public void handle(PaymentRequest request) {
// Zahlung ausführen und Ergebnis publizieren
}
}
#21.4 Outbox Pattern
Das Outbox Pattern löst ein klassisches Problem: Du willst Datenbankänderung und Event-Publishing konsistent halten. Statt direkt nach Kafka zu schreiben, speicherst du ein Event in einer Outbox-Tabelle innerhalb derselben Transaktion.
create table outbox_event (
id uuid primary key,
aggregate_type varchar(100) not null,
aggregate_id varchar(100) not null,
event_type varchar(100) not null,
payload jsonb not null,
created_at timestamp not null,
published_at timestamp null
);
#21.5 Übung
Baue einen Event-Flow:
order-servicespeichert Bestellung und Outbox Event.- Outbox Publisher sendet
OrderCreatednach Kafka. billing-servicekonsumiert das Event.- Bei Fehlern landet das Event in einer Dead Letter Queue oder einem Retry Topic.
- Metriken zeigen verarbeitete und fehlerhafte Events.
#22. Testing-Lernpfad: Von Unit Tests bis Architekturtests
Testing ist ein eigener Lernpfad. In Enterprise-Projekten brauchst du nicht nur Unit Tests, sondern eine Teststrategie.
#22.1 Testpyramide für Enterprise Java
| Ebene | Tools | Ziel |
|---|---|---|
| Unit Tests | JUnit, Mockito, AssertJ | Geschäftslogik schnell prüfen |
| Slice Tests | Spring MVC Test, Data JPA Test | einzelne technische Schichten prüfen |
| Integrationstests | Testcontainers, Quarkus Test, Arquillian | echte Datenbanken, Broker, Server prüfen |
| Contract Tests | Pact, Spring Cloud Contract | Service-Verträge absichern |
| End-to-End Tests | Playwright, Selenium, API Tests | kritische Nutzerflüsse prüfen |
| Architekturtests | ArchUnit | Modulgrenzen und Abhängigkeitsregeln prüfen |
| Mutation Tests | PIT | Qualität der Tests messen |
#22.2 JUnit + AssertJ Beispiel
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class PriceCalculatorTest {
@Test
void appliesDiscount() {
Money result = new PriceCalculator().applyDiscount(Money.eur(100), 10);
assertThat(result).isEqualTo(Money.eur(90));
}
}
#22.3 Testcontainers mit PostgreSQL
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.PostgreSQLContainer;
class OrderRepositoryIT {
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16");
@Test
void savesOrder() {
postgres.start();
String jdbcUrl = postgres.getJdbcUrl();
// Repository gegen echte PostgreSQL testen
}
}
#22.4 WireMock für externe HTTP APIs
stubFor(get(urlEqualTo("/customers/42"))
.willReturn(okJson("""
{ "id": "42", "name": "Ada" }
""")));
#22.5 ArchUnit Beispiel
import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.lang.ArchRule;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
@AnalyzeClasses(packages = "com.example")
class ArchitectureTest {
@ArchTest
static final ArchRule controllersShouldNotAccessRepositories =
classes().that().resideInAPackage("..web..")
.should().onlyAccessClassesThat()
.resideOutsideOfPackage("..repository..");
}
#22.6 Teststrategie dokumentieren
Für jedes Projekt solltest du eine TESTING.md pflegen:
# Teststrategie
## Ziel
Welche Risiken sollen Tests absichern?
## Testarten
- Unit Tests
- Integrationstests
- Contract Tests
- Architekturtests
## Was wird nicht automatisiert?
## Lokale Ausführung
## CI-Ausführung
## Testdaten
## Flaky-Test-Regeln
#23. Observability-Lernpfad: Logs, Metriken, Traces
Observability bedeutet, dass du das Verhalten eines Systems aus seinen Ausgaben verstehen kannst. In modernen Enterprise-Systemen brauchst du Logs, Metriken und Traces zusammen.
#23.1 Drei Säulen
| Signal | Frage | Beispiel |
|---|---|---|
| Logs | Was ist passiert? | Fehlermeldung mit Correlation ID |
| Metriken | Wie oft und wie stark? | Request Rate, Error Rate, Latenz |
| Traces | Wo war die Anfrage unterwegs? | Gateway → Order → Billing → Database |
#23.2 Spring Boot Actuator
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
probes:
enabled: true
#23.3 OpenTelemetry Java Agent
java \
-javaagent:opentelemetry-javaagent.jar \
-Dotel.service.name=order-service \
-Dotel.exporter.otlp.endpoint=http://otel-collector:4317 \
-jar order-service.jar
#23.4 Logging-Regeln
- Jede eingehende Anfrage bekommt eine Correlation ID.
- Logs sind strukturiert, zum Beispiel JSON.
- Keine Passwörter, Tokens oder personenbezogenen Daten loggen.
- Business Events und technische Logs trennen.
- Fehler mit Kontext loggen, aber nicht mehrfach in jeder Schicht.
#23.5 Metriken, die jedes Backend haben sollte
| Metrik | Warum wichtig? |
|---|---|
| HTTP Request Count | Last und Traffic verstehen |
| HTTP Error Rate | Fehleranstieg erkennen |
| Latenz p95/p99 | Nutzererfahrung und SLOs überwachen |
| DB Connection Pool Usage | Engpässe erkennen |
| Kafka Consumer Lag | Rückstau erkennen |
| JVM Heap/GC | Speicherprobleme analysieren |
| Thread Count | Blockaden oder Leaks erkennen |
#24. Cloud-native Deployment: Docker, Kubernetes, Helm und GitOps
Cloud-native Java bedeutet nicht nur „JAR in Container packen“. Du musst Konfiguration, Health Checks, Ressourcenlimits, Rollouts, Secrets und Observability beherrschen.
#24.1 Dockerfile für Spring Boot
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY target/order-service.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
#24.2 Kubernetes Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 2
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: order-service
image: registry.example.com/order-service:1.0.0
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "1000m"
#24.3 Kubernetes Service
apiVersion: v1
kind: Service
metadata:
name: order-service
spec:
selector:
app: order-service
ports:
- port: 80
targetPort: 8080
#24.4 Helm lernen
Lerne Helm erst, wenn du einfache Manifeste verstanden hast. Helm löst Wiederverwendung und Parametrisierung, kann aber Komplexität verstecken.
Lernziele:
- Chart-Struktur
values.yaml- Templates
- Releases
- Rollback
- Umgebungswerte pro Stage
#24.5 GitOps lernen
GitOps bedeutet: Git ist die deklarative Quelle für den Zielzustand. Tools wie Argo CD oder Flux gleichen Cluster-Zustand mit Git ab.
Übung:
- Lege ein Repository
platform-configan. - Speichere Kubernetes-Manifeste für
devundstage. - Simuliere eine Versionserhöhung im Image Tag.
- Dokumentiere den Rollout-Prozess.
#25. Build, CI/CD und Qualitätssicherung
Enterprise-Builds müssen reproduzierbar, nachvollziehbar und sicher sein.
#25.1 Maven und Gradle
| Thema | Maven | Gradle |
|---|---|---|
| Konvention | sehr stark | flexibel |
| Enterprise-Nachvollziehbarkeit | sehr gut | gut, aber Build-Logik kann komplex werden |
| Multi-Module | Standard | sehr mächtig |
| Performance | gut | oft sehr gut mit Build Cache |
| Lernkurve | niedriger | höher |
#25.2 CI/CD Pipeline Stufen
commit
-> compile
-> unit tests
-> static analysis
-> integration tests
-> package
-> container image
-> security scan
-> deploy to dev
-> smoke tests
-> approval
-> deploy to stage/prod
#25.3 GitHub Actions Beispiel
name: build
on:
push:
branches: [ main ]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '21'
cache: maven
- run: mvn -B clean verify
#25.4 Maven Quality Gate
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<executions>
<execution>
<goals><goal>enforce</goal></goals>
<configuration>
<rules>
<requireJavaVersion>
<version>[21,)</version>
</requireJavaVersion>
<dependencyConvergence />
</rules>
</configuration>
</execution>
</executions>
</plugin>
#25.5 Security in CI
- Dependency Scanning.
- Container Image Scanning.
- Secret Scanning.
- SBOM erzeugen.
- Signierte Artefakte.
- Reproduzierbare Builds.
- Keine Deployments direkt von Entwicklerlaptops.
#26. Performance und moderne JVM: Virtual Threads, GraalVM Native Image, Profiling
Performance beginnt nicht bei Optimierung, sondern bei Messbarkeit. Ohne Metriken, Profiler und Lasttests optimierst du oft die falsche Stelle.
#26.1 Virtual Threads
Virtual Threads sind besonders interessant für blockierende IO-Workloads, zum Beispiel klassische REST-APIs mit Datenbankzugriff. Sie ersetzen aber keine Architekturentscheidungen und lösen keine Datenbankengpässe.
Lernziele:
- Unterschied Platform Thread vs. Virtual Thread.
- Blocking IO und Thread-per-request.
- Pinning verstehen.
- Connection Pools richtig dimensionieren.
- Lasttest vor und nach Änderung.
#26.2 Spring Boot Virtual Threads Beispiel
spring:
threads:
virtual:
enabled: true
#26.3 Native Image
GraalVM Native Image kann Java-Anwendungen Ahead-of-Time in native Executables kompilieren. Das kann Startzeit und Speicherverbrauch verbessern, bringt aber neue Build- und Kompatibilitätsfragen mit sich.
mvn -Pnative native:compile
#26.4 Wann Native Image sinnvoll ist
| Sinnvoll | Vorsicht |
|---|---|
| serverless Funktionen | große, klassische Enterprise-App mit viel Reflection |
| kurzlebige Jobs | wenig Nutzen bei sehr langen Laufzeiten |
| viele kleine Microservices | Build-Zeit kann steigen |
| Startup kritisch | Debugging und Profiling anders |
#26.5 Profiling-Lernpfad
- Logs und Metriken prüfen.
- JVM-Parameter verstehen.
- Heap Dump analysieren.
- Thread Dump lesen.
- Java Flight Recorder nutzen.
- Datenbank-Query-Plan prüfen.
- Lasttests mit k6, Gatling oder JMeter durchführen.
#27. Architekturpatterns für Enterprise Java
Framework-Wissen reicht nicht. Du musst Entscheidungen begründen können.
#27.1 Layered Architecture
Klassische Schichten:
Controller / Resource
Service / Use Case
Domain
Repository / DAO
Infrastructure
Vorteil: einfach zu verstehen. Nachteil: Domäne wird oft an Framework und Datenbankmodell gekoppelt.
#27.2 Hexagonale Architektur
Inbound Adapter -> Application Port -> Domain -> Outbound Port -> Outbound Adapter
REST Controller Use Case Interface Model Repository Port JPA Adapter
Vorteil: Fachlogik ist unabhängiger von Frameworks. Nachteil: mehr Klassen und Disziplin nötig.
#27.3 Modulith
Ein Modulith ist ein modularer Monolith: Deployment bleibt einfach, aber Code ist fachlich getrennt. Das ist oft besser als zu frühe Microservices.
Lernübung:
- Erstelle Module
customer,order,billing. - Verhindere direkte Zugriffe auf interne Pakete.
- Kommuniziere zwischen Modulen über Events oder klar definierte APIs.
- Prüfe Regeln mit ArchUnit.
#27.4 Microservices
Microservices sind sinnvoll, wenn Teams, Deployment-Zyklen und Skalierungsanforderungen unabhängig sind. Sie sind nicht sinnvoll, nur weil ein Framework es einfach macht.
Checkliste vor Microservices:
- Hat jedes Team Ownership?
- Gibt es CI/CD pro Service?
- Gibt es Observability?
- Gibt es eine Security-Strategie?
- Gibt es Datenbesitz pro Service?
- Sind Konsistenzanforderungen verstanden?
- Gibt es eine Plattform für Deployment und Betrieb?
#27.5 DDD-Grundlagen
| Begriff | Bedeutung |
|---|---|
| Entity | Objekt mit Identität |
| Value Object | Objekt ohne Identität, über Werte gleich |
| Aggregate | Konsistenzgrenze |
| Repository | Zugriff auf Aggregate |
| Domain Event | fachliches Ereignis |
| Bounded Context | fachliche Modellgrenze |
| Ubiquitous Language | gemeinsame Sprache von Fachbereich und Technik |
#28. Daten- und Integrationspatterns
#28.1 Transaktionaler Monolith
Ein Monolith kann mit einer relationalen Datenbank und ACID-Transaktionen sehr robust sein. Das ist kein Fehler, sondern oft die richtige Anfangsarchitektur.
#28.2 Saga Pattern
Eine Saga koordiniert mehrere lokale Transaktionen. Es gibt zwei Varianten:
| Variante | Erklärung |
|---|---|
| Choreography | Services reagieren auf Events, keine zentrale Steuerung |
| Orchestration | ein Orchestrator steuert die Schritte |
#28.3 Idempotenz
Idempotenz bedeutet: dieselbe Operation kann mehrfach ausgeführt werden, ohne mehrfachen fachlichen Schaden zu verursachen.
create table idempotency_key (
key varchar(200) primary key,
response_hash varchar(200),
created_at timestamp not null
);
#28.4 CQRS
Command Query Responsibility Segregation trennt Schreibmodell und Lesemodell. Das lohnt sich, wenn Lese- und Schreibanforderungen stark unterschiedlich sind.
#28.5 Event Sourcing
Bei Event Sourcing speicherst du Ereignisse als Quelle der Wahrheit. Das ist mächtig, aber komplex. Lerne es erst, wenn du Events, Idempotenz und Projektionen verstanden hast.
#28.6 Anti-Pattern
| Anti-Pattern | Warum problematisch |
|---|---|
| Shared Database zwischen Microservices | Services sind nicht wirklich unabhängig |
| Chatty Services | zu viele synchrone Calls, hohe Latenz |
| Event ohne Schema Governance | Consumer brechen bei Änderungen |
| Retry Storm | Fehler wird durch Wiederholungen verstärkt |
| Distributed Transaction als Standardlösung | schwer skalierbar und operativ komplex |
#29. Migration-Lernpfade
#29.1 Java EE / javax.* zu Jakarta EE / jakarta.*
Viele ältere Enterprise-Projekte nutzen noch javax.*. Moderne Jakarta-EE-Versionen nutzen jakarta.*.
Vorgehen:
- Inventar erstellen: Dependencies, App Server, APIs, Build Plugins.
- Prüfen, welche Framework-Versionen
jakarta.*unterstützen. - Automatisierte Imports umstellen.
- Tests ausführen.
- Deployment Descriptor prüfen.
- Application Server aktualisieren.
- Smoke Tests und Regressionstests ausführen.
#29.2 Spring Boot Major Migration
Spring-Migrationen solltest du nicht „blind“ durchführen.
Checkliste:
- Release Notes lesen.
- Java-Version prüfen.
- Jakarta Namespace prüfen.
- Deprecated APIs ersetzen.
- Security-Konfiguration prüfen.
- Actuator-Endpoint-Namen prüfen.
- Test-Slices prüfen.
- Container Image und Runtime prüfen.
#29.3 Monolith zu Modulith
Bevor du Microservices baust, extrahiere fachliche Module.
com.example.shop
├── customer
│ ├── api
│ ├── internal
│ └── CustomerModule.java
├── order
│ ├── api
│ ├── internal
│ └── OrderModule.java
└── billing
├── api
├── internal
└── BillingModule.java
#29.4 App Server zu Container
Migration von klassischem Application Server zu Container-Deployment:
| Alt | Neu |
|---|---|
| EAR/WAR auf zentralem Server | einzelner Service im Container |
| JNDI-Datasource | Konfiguration über Environment/Secrets |
| Serverweite Libraries | app-spezifische Dependencies |
| manuelles Deployment | CI/CD Pipeline |
| Server-Logs | zentrale Logplattform |
| lokale Admin-Konsole | Kubernetes/GitOps/Monitoring |
#30. Framework-Entscheidungsmatrix
#30.1 Entscheidung nach Projektart
| Projektart | Empfehlung |
|---|---|
| klassischer Enterprise-CRUD-Service | Spring Boot oder Jakarta EE |
| neues Cloud-native Microservice-System | Spring Boot, Quarkus oder Micronaut evaluieren |
| standardisierte App-Server-Landschaft | Jakarta EE + MicroProfile |
| sehr schnelle Startzeit wichtig | Quarkus, Micronaut oder Spring Native/AOT prüfen |
| Team hat viel Spring-Wissen | Spring Boot als Standard, Quarkus/Micronaut gezielt evaluieren |
| Team hat viel Java-EE/Jakarta-Wissen | Jakarta EE, MicroProfile, Quarkus oder Helidon MP |
| Event Streaming Plattform | Kafka + Framework-Integration |
| viele Integrationsrouten | Apache Camel oder Spring Integration |
#30.2 Bewertungsraster
Bewerte jedes Framework mit 1 bis 5 Punkten:
| Kriterium | Gewicht | Spring Boot | Jakarta EE | Quarkus | Micronaut | Helidon |
|---|---|---|---|---|---|---|
| Team-Wissen | 5 | |||||
| Ökosystem | 5 | |||||
| Betriebsmodell | 4 | |||||
| Startzeit/Speicher | 3 | |||||
| Standardisierung | 4 | |||||
| Cloud/Kubernetes | 4 | |||||
| Security-Integration | 5 | |||||
| Testing-Support | 5 | |||||
| Langfristige Wartbarkeit | 5 |
#30.3 Entscheidungsregel
Die beste Technologie ist selten die mit den meisten Features. In Enterprise-Projekten gewinnt oft die Technologie, die das Team zuverlässig betreiben, testen, aktualisieren und erklären kann.
#31. Erweiterter 24-Wochen-Lernplan
#Phase 1: Fundament, Wochen 1–4
| Woche | Fokus | Ergebnis |
|---|---|---|
| 1 | Java 17/21, Maven, HTTP, SQL | kleine CLI-App + REST-Grundlagen verstanden |
| 2 | Dependency Injection | Spring Bean + CDI Bean erklären können |
| 3 | REST APIs | Spring MVC und Jakarta REST Minimal-API |
| 4 | JPA und Transaktionen | Entity, Repository/DAO, Migration |
#Phase 2: Spring und Jakarta produktiv, Wochen 5–8
| Woche | Fokus | Ergebnis |
|---|---|---|
| 5 | Spring Boot intern | Auto-Configuration, Properties, Profiles |
| 6 | Jakarta EE intern | CDI, Scopes, App Server Deployment |
| 7 | Security | OAuth2 Resource Server + Rollenmodell |
| 8 | Testing | Unit, Integration, Testcontainers |
#Phase 3: Erweiterte Frameworks, Wochen 9–12
| Woche | Fokus | Ergebnis |
|---|---|---|
| 9 | MicroProfile | Config, Health, OpenAPI, Rest Client |
| 10 | Quarkus | REST, CDI, Hibernate, Tests, Container |
| 11 | Micronaut | Controller, DI, Config, HTTP Client |
| 12 | Helidon | SE vs. MP, MicroProfile-Anwendung |
#Phase 4: Enterprise Integration, Wochen 13–16
| Woche | Fokus | Ergebnis |
|---|---|---|
| 13 | Kafka und Events | Producer/Consumer + Outbox |
| 14 | RabbitMQ/JMS | Queue, Retry, DLQ |
| 15 | API Contracts | OpenAPI, Contract Tests, Versionierung |
| 16 | Integration Patterns | Camel/Spring Integration Überblick |
#Phase 5: Betrieb, Cloud und Qualität, Wochen 17–20
| Woche | Fokus | Ergebnis |
|---|---|---|
| 17 | Docker | Container Image, Compose, lokale Umgebung |
| 18 | Kubernetes | Deployment, Service, Probes, ConfigMap, Secret |
| 19 | Observability | Actuator, Micrometer, OpenTelemetry |
| 20 | CI/CD | Pipeline, Quality Gate, Security Scan |
#Phase 6: Architektur und Abschluss, Wochen 21–24
| Woche | Fokus | Ergebnis |
|---|---|---|
| 21 | Modulith und Hexagonal | modulare Beispielanwendung |
| 22 | Microservices und DDD | Bounded Contexts, Events, Saga |
| 23 | Migration | javax zu jakarta, App Server zu Container |
| 24 | Abschlussprojekt | vollständiges Enterprise-Java-System |
#32. Abschlussprojekt: Enterprise Shop Plattform
Baue ein kleines, aber realistisches System:
enterprise-shop/
├── customer-service # Spring Boot
├── order-service # Quarkus oder Spring Boot
├── inventory-service # Jakarta EE oder Helidon MP
├── billing-service # Micronaut oder Spring Boot
├── platform-config # Kubernetes/Helm/GitOps
├── docs # Architekturentscheidungen
└── docker-compose.yml
#32.1 Muss-Anforderungen
- REST APIs mit OpenAPI.
- PostgreSQL pro Service.
- Flyway oder Liquibase Migrationen.
- Security mit OAuth2/OIDC.
- Events über Kafka oder RabbitMQ.
- Integrationstests mit Testcontainers.
- Observability mit Logs, Metriken und Traces.
- Docker Compose für lokale Entwicklung.
- Kubernetes-Manifeste für Deployment.
- CI Pipeline mit Tests und Quality Gate.
#32.2 Erweiterte Anforderungen
- Outbox Pattern.
- Idempotency Keys.
- Contract Tests.
- ArchUnit-Regeln.
- Load Test.
- SBOM.
- Native Image für mindestens einen Service.
- Migration eines Service von Spring Boot zu Quarkus oder umgekehrt.
#32.3 Dokumentationsartefakte
Erstelle im Projektordner:
docs/
├── architecture.md
├── api-guidelines.md
├── testing-strategy.md
├── security-model.md
├── deployment.md
├── observability.md
└── adr/
├── 0001-framework-selection.md
├── 0002-database-per-service.md
└── 0003-eventing-strategy.md
#33. Lernkarten: Was du mündlich erklären können solltest
#33.1 Spring
- Was ist der Unterschied zwischen
@Component,@Serviceund@Bean? - Wie funktioniert Spring Boot Auto-Configuration?
- Was ist ein Starter?
- Wie funktioniert Spring Security Filter Chain?
- Was ist der Unterschied zwischen
@Transactionalauf Service- und Repository-Ebene? - Wie funktionieren Spring Profiles?
- Wofür ist Actuator?
#33.2 Jakarta EE
- Was ist CDI?
- Was ist der Unterschied zwischen
@ApplicationScopedund@RequestScoped? - Wie funktioniert Jakarta REST?
- Was ist JTA?
- Was ist eine Persistence Unit?
- Was bedeutet Deployment auf einem Application Server?
- Was ist der Unterschied zwischen Web Profile und Platform?
#33.3 Cloud-native Frameworks
- Warum optimiert Quarkus zur Build-Zeit?
- Was bedeutet Compile-Time DI in Micronaut?
- Wann ist Helidon MP sinnvoller als Helidon SE?
- Was ist MicroProfile Config?
- Was ist der Unterschied zwischen Health, Metrics und Telemetry?
#33.4 Architektur
- Wann ist ein Modulith besser als Microservices?
- Was ist ein Bounded Context?
- Warum sind verteilte Transaktionen schwierig?
- Was löst das Outbox Pattern?
- Wie testet man Service-Verträge?
- Warum ist Observability Teil der Architektur?
#34. Komplexes modulares Beispielprojekt: Enterprise Commerce Platform
Dieses Kapitel ergänzt den Lernpfad um ein größeres Beispielprojekt, das typische Enterprise-Java-Bausteine zusammenführt. Es ist kein simples Demo-Projekt, sondern eine bewusst modulare Lernplattform mit mehreren Services, gemeinsamen Libraries, Maven Parent-POM, Docker Compose, Datenbanken, Kafka, API Gateway, Events und Deployment-Vorlagen.
Das Projekt liegt zusätzlich als eigener Ordner enterprise-commerce-platform/ im ZIP-Paket. Die Codeauszüge in diesem Kapitel erklären die wichtigsten Stellen; der vollständige Code befindet sich im Projektordner.
#34.1 Fachliches Szenario
Die Plattform simuliert einen kleinen Enterprise-Shop:
- Kunden werden im
customer-serviceverwaltet. - Produkte und Lagerbestand liegen im
catalog-service. - Bestellungen werden im
order-serviceerzeugt. - Zahlungen werden im
payment-serviceautorisiert. - Benachrichtigungen werden im
notification-serviceasynchron verarbeitet. - Externe Clients gehen über das
api-gateway.
Das Szenario ist absichtlich so gewählt, dass du viele Enterprise-Themen üben kannst: Modulgrenzen, REST, Events, Datenbank pro Service, Transaktionen, Event Contracts, Build-Struktur, Observability und lokale Infrastruktur.
#34.2 Architekturübersicht
Browser / API Client
|
v
api-gateway
|
+--> customer-service
+--> catalog-service
+--> order-service ----> payment-service
| |
+---- Kafka ------+
|
v
notification-service
Wichtig ist: Jeder Service besitzt sein eigenes Datenmodell. Der order-service darf nicht direkt in die Datenbank des catalog-service greifen. Stattdessen ruft er eine API auf. Nachgelagerte Reaktionen laufen über Events.
#34.3 Projektstruktur
enterprise-commerce-platform/
├── pom.xml
├── docker-compose.yml
├── docker/
│ └── postgres/init.sql
├── docs/
│ ├── architecture.md
│ └── adr/0001-service-boundaries.md
├── libs/
│ ├── shared-kernel/
│ ├── event-contracts/
│ ├── web-starter/
│ └── test-support/
├── services/
│ ├── api-gateway/
│ ├── customer-service/
│ ├── catalog-service/
│ ├── order-service/
│ ├── payment-service/
│ └── notification-service/
├── deployments/
│ └── k8s/order-service-deployment.yaml
└── scripts/
└── smoke-test.http
Diese Struktur zeigt eine häufige Enterprise-Aufteilung:
| Bereich | Zweck |
|---|---|
libs/shared-kernel |
technische Basistypen wie Money, DomainEvent, IdempotencyKey |
libs/event-contracts |
stabile Event-Verträge zwischen Services |
libs/web-starter |
gemeinsame REST-Fehlerbehandlung |
libs/test-support |
Testcontainers- und Integrationstest-Basis |
services/* |
fachliche Microservices |
deployments/* |
Kubernetes- und Betriebsartefakte |
docs/* |
Architekturentscheidungen und technische Dokumentation |
#34.4 Maven Parent-POM
Die Root-POM ist Aggregator und Build-Parent. Sie definiert Module, Java-Version, Spring-Boot-Version, Spring-Cloud-BOM und zentrale Plugin-Versionen.
<project>
<groupId>com.acme.platform</groupId>
<artifactId>enterprise-commerce-platform</artifactId>
<version>${revision}</version>
<packaging>pom</packaging>
<modules>
<module>libs/shared-kernel</module>
<module>libs/event-contracts</module>
<module>libs/web-starter</module>
<module>libs/test-support</module>
<module>services/customer-service</module>
<module>services/catalog-service</module>
<module>services/payment-service</module>
<module>services/order-service</module>
<module>services/notification-service</module>
<module>services/api-gateway</module>
</modules>
<properties>
<revision>1.0.0-SNAPSHOT</revision>
<java.version>21</java.version>
<spring-boot.version>3.5.15</spring-boot.version>
<spring-cloud.version>2025.0.0</spring-cloud.version>
</properties>
</project>
Warum das wichtig ist:
- Alle Services verwenden dieselbe Java-Version.
- Dependency-Versionen werden zentral verwaltet.
- Maven Reactor baut Libraries vor Services.
- CI/CD kann einzelne Services oder das komplette System bauen.
#34.5 Warum gemeinsame Libraries vorsichtig verwenden?
Gemeinsame Libraries sind nützlich, aber gefährlich, wenn darin Fachlogik landet. In diesem Beispiel gelten diese Regeln:
| Library | Erlaubt | Nicht erlaubt |
|---|---|---|
shared-kernel |
technische Value Objects, kleine Interfaces | Customer-, Order- oder Payment-Fachlogik |
event-contracts |
Event DTOs und Versionierung | Datenbank-Entities oder Service-Abhängigkeiten |
web-starter |
technische Fehlerbehandlung | fachliche Controller-Logik |
test-support |
Testcontainers-Basis | produktiver Code |
Beispiel Money aus shared-kernel:
public record Money(BigDecimal amount, String currency) {
public Money {
Objects.requireNonNull(amount, "amount must not be null");
Objects.requireNonNull(currency, "currency must not be null");
Currency.getInstance(currency);
amount = amount.setScale(2, RoundingMode.HALF_UP);
if (amount.signum() < 0) {
throw new IllegalArgumentException("amount must be positive");
}
}
}
Das ist technische, wiederverwendbare Logik. Dagegen sollte eine Regel wie „Premiumkunden erhalten 10 Prozent Rabatt“ nicht in shared-kernel, sondern in den fachlich zuständigen Service.
#34.6 Event Contracts
Events sind Verträge zwischen Services. Deshalb liegen sie in libs/event-contracts und werden bewusst versioniert.
public record OrderCreatedEvent(
String eventId,
String aggregateId,
String customerId,
List<OrderLineEvent> lines,
BigDecimal totalAmount,
String currency,
Instant occurredAt
) implements DomainEvent {
@Override
public String type() {
return "order.created.v1";
}
}
Lernpunkte:
eventIdhilft bei Idempotenz und Deduplizierung.aggregateIdverweist auf die betroffene Bestellung.type()enthält eine explizite Event-Version.- Events sollten möglichst additive Änderungen bekommen, damit Consumer nicht brechen.
#34.7 Customer Service
Der customer-service ist bewusst einfach gehalten. Er besitzt Kundendaten und stellt REST-Endpunkte bereit.
@RestController
@RequestMapping("/customers")
public class CustomerController {
private final CustomerApplicationService service;
public CustomerController(CustomerApplicationService service) {
this.service = service;
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
CustomerResponse create(@Valid @RequestBody CustomerRequest request) {
return service.create(request);
}
@GetMapping("/{id}")
CustomerResponse get(@PathVariable UUID id) {
return service.get(id);
}
}
Die Service-Schicht kapselt die Transaktion:
@Service
public class CustomerApplicationService {
private final CustomerRepository repository;
@Transactional
public CustomerResponse create(CustomerRequest request) {
if (repository.existsByEmail(request.email())) {
throw new IllegalArgumentException("email already exists");
}
Customer customer = repository.save(new Customer(request.name(), request.email()));
return map(customer);
}
}
Wichtig: Der Controller enthält keine Datenbanklogik. Die Transaktion sitzt in der Anwendungsschicht.
#34.8 Catalog Service
Der catalog-service besitzt Produkte und Lagerbestand. Der order-service reserviert Bestand über eine REST-API.
public void reserve(int quantity) {
if (quantity <= 0) throw new IllegalArgumentException("quantity must be positive");
if (availableStock < quantity) throw new IllegalArgumentException("not enough stock for sku " + sku);
availableStock -= quantity;
}
Das ist eine fachliche Regel und gehört deshalb in das Domain-Objekt Product, nicht in den Controller.
#34.9 Order Service als Orchestrator
Der order-service ist der komplexeste Service. Er koordiniert mehrere Schritte:
- Kunde existiert?
- Lagerbestand reservieren.
- Bestellung speichern.
- Zahlung autorisieren.
OrderCreatedEventpublizieren.
@Transactional
public OrderResponse create(CreateOrderRequest request) {
customerClient.assertExists(request.customerId());
OrderEntity order = new OrderEntity(request.customerId());
request.lines().forEach(line -> {
CatalogClient.Product product = catalogClient.reserve(line.sku(), line.quantity());
order.addLine(product.sku(), line.quantity(), product.price());
});
order = repository.save(order);
paymentClient.authorize(order.id(), order.totalAmount());
OrderCreatedEvent event = new OrderCreatedEvent(
UUID.randomUUID().toString(),
order.id().toString(),
order.customerId().toString(),
order.lines().stream()
.map(l -> new OrderLineEvent(l.sku(), l.quantity(), l.unitPrice()))
.toList(),
order.totalAmount(),
"EUR",
Instant.now());
kafkaTemplate.send("order-events", order.id().toString(), event);
return new OrderResponse(order.id(), order.customerId(), order.totalAmount(), order.status());
}
Dieses Beispiel zeigt absichtlich eine erste, verständliche Orchestrierung. In einer produktionsnahen nächsten Ausbaustufe würdest du ergänzen:
- Outbox Pattern statt direktem Kafka-Senden in der Transaktion.
- Idempotency Key für
POST /orders. - Retry und Circuit Breaker für Remote Calls.
- Kompensationslogik, falls Zahlung fehlschlägt.
- Contract Tests für
customer-service,catalog-serviceundpayment-service.
#34.10 Payment Service
Der payment-service ist ein Beispiel für einen fachlich separaten Service. In echten Systemen würde er Payment Provider, PSP-Callbacks und Fraud Checks integrieren. Im Lernprojekt autorisiert er eine Zahlung und publiziert ein Event.
@Transactional
public PaymentAuthorizationResponse authorize(PaymentAuthorizationRequest request) {
Payment payment = repository.save(Payment.authorized(request.orderId(), request.amount(), request.currency()));
kafkaTemplate.send("payment-events", request.orderId(), new PaymentAuthorizedEvent(
UUID.randomUUID().toString(), request.orderId(), payment.id().toString(),
request.amount(), request.currency(), Instant.now()));
return new PaymentAuthorizationResponse(payment.id().toString(), payment.status());
}
#34.11 Notification Service als Event Consumer
Der notification-service reagiert auf OrderCreatedEvent. Er wird nicht synchron vom order-service aufgerufen. Das ist ein typisches Beispiel für Entkopplung.
@KafkaListener(topics = "order-events")
public void onOrderCreated(OrderCreatedEvent event) {
log.info("Prepare notification for order {} and customer {}", event.aggregateId(), event.customerId());
repository.save(new NotificationLog(event.aggregateId(), event.type()));
}
Vorteil: Wenn der Notification-Service kurz ausfällt, kann Kafka Events puffern. Der Bestellprozess muss nicht direkt von einer E-Mail-Komponente abhängen.
#34.12 API Gateway
Das API Gateway routet externe Requests auf interne Services.
spring:
cloud:
gateway:
routes:
- id: customer-service
uri: http://localhost:8081
predicates:
- Path=/customers/**
- id: catalog-service
uri: http://localhost:8082
predicates:
- Path=/products/**
- id: order-service
uri: http://localhost:8083
predicates:
- Path=/orders/**
In einer nächsten Ausbaustufe kannst du hier ergänzen:
- OAuth2 Resource Server
- Rate Limiting
- Request Logging
- Correlation IDs
- CORS-Regeln
- API-Versionierung
#34.13 Lokale Infrastruktur mit Docker Compose
Das Beispielprojekt enthält PostgreSQL und Kafka für lokale Entwicklung.
services:
postgres:
image: postgres:16
environment:
POSTGRES_USER: acme
POSTGRES_PASSWORD: acme
POSTGRES_DB: platform
ports:
- "5432:5432"
kafka:
image: bitnami/kafka:3.7
ports:
- "9092:9092"
Start:
cd enterprise-commerce-platform
docker compose up -d postgres kafka kafka-ui
mvn clean verify
mvn -pl services/customer-service -am spring-boot:run
#34.14 Smoke-Test-Ablauf
Das Projekt enthält scripts/smoke-test.http. Der Ablauf ist:
- Kunde anlegen.
- Produkte abrufen.
- Bestellung mit Produktpositionen erzeugen.
- Kafka UI öffnen und
order-eventsprüfen. - Notification-Datenbank prüfen.
Beispiel Request:
POST http://localhost:8080/orders
Content-Type: application/json
{
"customerId": "REPLACE_WITH_CUSTOMER_ID",
"lines": [
{ "sku": "BOOK-SPRING", "quantity": 1 },
{ "sku": "BOOK-JAKARTA", "quantity": 1 }
]
}
#34.15 Kubernetes-Beispiel
Ein einfacher Deployment-Ausschnitt zeigt, wie der order-service produktionsnah betrieben werden könnte.
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 2
template:
spec:
containers:
- name: order-service
image: ghcr.io/acme/order-service:1.0.0
ports:
- containerPort: 8083
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8083
Das ist bewusst nur ein Startpunkt. Für echte Umgebungen fehlen noch Secrets, ConfigMaps, Resource Limits, Network Policies, Ingress, Service Account und Observability-Konfiguration.
#34.16 Erweiterungsaufgaben
Nutze das Beispielprojekt als Trainingsbasis. Baue es in Stufen aus:
#Stufe 1: Solide REST-Services
- CRUD für Customers und Products vervollständigen.
- Pagination einbauen.
- Fehlerantworten mit
ProblemDetailoder eigenerApiError-Struktur vereinheitlichen. - OpenAPI/Swagger ergänzen.
#Stufe 2: Robustere Microservices
- Resilience4j für Timeouts, Retry und Circuit Breaker einbauen.
- Idempotency Key für
POST /ordersergänzen. - Outbox Pattern im
order-serviceimplementieren. - Dead Letter Topic für Kafka einrichten.
#Stufe 3: Security
- Keycloak lokal ergänzen.
- Gateway als OAuth2 Resource Server konfigurieren.
- Service-to-Service-Kommunikation absichern.
- Rollenmodell definieren:
CUSTOMER,ADMIN,SUPPORT.
#Stufe 4: Testing
- Testcontainers für PostgreSQL verwenden.
- WireMock für Remote Services einsetzen.
- Contract Tests zwischen
order-serviceundpayment-serviceergänzen. - ArchUnit-Regeln für Schichten und Paketgrenzen schreiben.
#Stufe 5: Observability
- Correlation ID pro Request erzeugen.
- Micrometer-Metriken ergänzen.
- OpenTelemetry Tracing einbauen.
- Grafana/Prometheus/Loki in Docker Compose ergänzen.
#Stufe 6: Alternative Frameworks
Setze einen Service zusätzlich in einer anderen Technologie um:
catalog-serviceals Quarkus-Service.notification-serviceals Micronaut-Service.payment-serviceals Jakarta EE/MicroProfile-Service.
So lernst du nicht nur Spring Boot, sondern erkennst Framework-Unterschiede anhand derselben fachlichen Domäne.
#34.17 Lernfragen zum Beispielprojekt
- Warum ist
event-contractsein eigenes Modul? - Warum darf der
order-servicenicht direkt in die Catalog-Datenbank schreiben? - Welche Risiken entstehen durch synchrone Calls im
order-service? - Warum ist direktes Kafka-Senden innerhalb einer Datenbanktransaktion problematisch?
- Welche Daten gehören in ein Event, welche nicht?
- Wann wäre ein modularer Monolith besser als diese Microservice-Struktur?
- Welche Teile würdest du zuerst produktionsreif machen?
- Wie würdest du Security, Observability und CI/CD ergänzen?
#35. Quellen und offizielle Dokumentation
Diese Quellen wurden zur fachlichen Einordnung und Aktualisierung des erweiterten Lernpfads verwendet:
Jakarta EE Specifications: https://jakarta.ee/specifications/↩
Spring Framework Documentation: https://docs.spring.io/spring-framework/reference/index.html↩
Spring Boot Documentation: https://docs.spring.io/spring-boot/documentation.html↩
Spring Cloud Reference Documentation: https://docs.spring.io/spring-cloud/docs/current/reference/html/↩
Jakarta EE Tutorial: https://jakarta.ee/learn/docs/jakartaee-tutorial/current/index.html↩