seb4u Java Master Workbook
Aufgaben, Lösungen, Review-Fragen und Portfolio-Routine passend zur finalen Modulstruktur und zum Maven-Multi-Module-Projektpaket.
So benutzt du das Workbook
1. Aufgabe zuerst
Lies die Aufgabe, schreibe API und Tests zuerst. Schaue nicht sofort in die Lösung.
2. Lösung vergleichen
Vergleiche nicht nur Syntax, sondern Invarianten, Fehlervertrag, Package und Modulgrenze.
3. Refactoring
Baue nach jeder Lösung eine kleine Verbesserung ein: bessere Namen, Test, Edge Case oder Performance.
Workbook
Modul A - Orientierung, Setup und Projektstandard
Du richtest dein Lernen sauber aus: Package-Regel, Maven-Struktur, Build-Checks und Lernroutine.
Aufgaben
- Package-Standard prüfen: Schreibe ein kleines Skript oder eine JUnit-Prüfung, die verbietet, dass eigene Klassen außerhalb von com.seb4u.demo, com.seb4u.demo.spring oder com.seb4u.demo.jakarta liegen.
- Lernroutine definieren: Plane pro Woche 3 Code-Sessions: Lesen, Nachbauen, Refactoring. Dokumentiere jeweils ein Fehlerbild.
Review-Fragen
- Welche Invariante schützt diese Lösung?
- Welche Fehler sind fachlich, welche technisch?
- Welche Klasse kennt zu viel?
- Welche Tests würden einen Regression-Bug sofort zeigen?
Musterlösung anzeigen
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | package com.seb4u.demo.workbook.core; import java.nio.file.*; import java.util.*; import java.util.regex.Pattern; public final class PackageGuardSolution { private static final Pattern ALLOWED = Pattern.compile( "package\\s+com\\.seb4u\\.demo(\\.(spring|jakarta))?(\\.[a-z0-9_]+)*\\s*;" ); public static List<Path> findInvalidJavaFiles(Path root) throws Exception { try (var stream = Files.walk(root)) { return stream.filter(p -> p.toString().endsWith(".java")) .filter(PackageGuardSolution::hasInvalidPackage) .toList(); } } private static boolean hasInvalidPackage(Path path) { try { String text = Files.readString(path); if (!text.contains("package ")) return false; // allowed for tiny snippets return !ALLOWED.matcher(text).find(); } catch (Exception ex) { throw new IllegalStateException("Cannot read " + path, ex); } } } |
Zusätzliche Aufgaben und Musterlösungen
Diese Praxisaufgaben gehören direkt zu diesem Modul. Bearbeite sie nach den ersten Aufgaben und vergleiche anschließend deine Lösung mit der Musterlösung.
PraxisReviewMusterloesung
| # | Aufgabe | Abgabe | Prueffragen |
|---|---|---|---|
| A.1 | Erstelle eine Projekt-README mit Lernziel, Build-Befehl, Modulgrenzen und Package-Regel. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| A.2 | Schreibe ein Shell-Skript, das verbotene Packages wie com.example und com.acme findet. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| A.3 | Definiere Maven-Profile fuer local, ci und release-dry-run und beschreibe die Unterschiede. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| A.4 | Baue eine Checkliste fuer neue Codebeispiele: Package, Testbarkeit, Fehlervertrag, Imports, Run-Anweisung. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| A.5 | Lege eine Ordnerkonvention fuer Handbuch, Workbook, Assessment, Projekt und Delivery fest. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
Musterloesung zu Modul A
Vergleiche deine Loesung nicht zeilenweise, sondern pruefe Architektur, Fehlerfaelle, Naming, Package-Regel und Testbarkeit.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | package com.seb4u.demo.tooling; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import java.util.regex.Pattern; public final class PackageGuard { private static final Pattern FORBIDDEN = Pattern.compile("package\s+(com\.example|com\.acme|org\.demo)\b"); public static void main(String[] args) throws IOException { Path root = args.length == 0 ? Path.of(".") : Path.of(args[0]); List<Path> violations = Files.walk(root) .filter(p -> p.toString().endsWith(".java")) .filter(PackageGuard::hasForbiddenPackage) .toList(); if (!violations.isEmpty()) { violations.forEach(p -> System.err.println("Forbidden package: " + p)); System.exit(2); } System.out.println("Package guard OK: " + root.toAbsolutePath()); } private static boolean hasForbiddenPackage(Path file) { try { return FORBIDDEN.matcher(Files.readString(file)).find(); } catch (IOException ex) { throw new IllegalStateException("Cannot read " + file, ex); } } } |
Workbook
Modul B - Core Java, Typsystem und Objektmodell
Du modellierst fachliche Regeln mit Records, sealed Types, Validierung und klaren Ergebnistypen.
Aufgaben
- Result-Typ erweitern: Baue Result<T> mit map, flatMap, recover und Fehlerliste.
- Value Object absichern: Implementiere Money so, dass Währung, Rundung und negative Beträge kontrolliert werden.
Review-Fragen
- Welche Invariante schützt diese Lösung?
- Welche Fehler sind fachlich, welche technisch?
- Welche Klasse kennt zu viel?
- Welche Tests würden einen Regression-Bug sofort zeigen?
Musterlösung anzeigen
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | package com.seb4u.demo.workbook.core; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.Currency; import java.util.Objects; import java.util.function.Function; public record Money(BigDecimal amount, Currency currency) implements Comparable<Money> { public Money { Objects.requireNonNull(amount, "amount"); Objects.requireNonNull(currency, "currency"); amount = amount.setScale(currency.getDefaultFractionDigits(), RoundingMode.HALF_UP); if (amount.signum() < 0) throw new IllegalArgumentException("Money must not be negative"); } public Money add(Money other) { requireSameCurrency(other); return new Money(amount.add(other.amount), currency); } public Money multiply(int factor) { if (factor < 0) throw new IllegalArgumentException("factor must not be negative"); return new Money(amount.multiply(BigDecimal.valueOf(factor)), currency); } private void requireSameCurrency(Money other) { if (!currency.equals(other.currency)) { throw new IllegalArgumentException("Currency mismatch: " + currency + " vs " + other.currency); } } @Override public int compareTo(Money other) { requireSameCurrency(other); return amount.compareTo(other.amount); } public static Money eur(String amount) { return new Money(new BigDecimal(amount), Currency.getInstance("EUR")); } } |
Zusätzliche Aufgaben und Musterlösungen
Diese Praxisaufgaben gehören direkt zu diesem Modul. Bearbeite sie nach den ersten Aufgaben und vergleiche anschließend deine Lösung mit der Musterlösung.
PraxisReviewMusterloesung
| # | Aufgabe | Abgabe | Prueffragen |
|---|---|---|---|
| B.1 | Modelliere Money als Value Object mit Waehrung, Rundung und Validierung. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| B.2 | Baue Typed IDs fuer CustomerId, OrderId und ProductId ohne String-Verwechslung. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| B.3 | Entwirf eine sealed State Machine fuer OrderStatus mit erlaubten Uebergaengen. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| B.4 | Vergleiche record, class und enum fuer fachliche Modelle und dokumentiere die Entscheidung. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| B.5 | Refactore eine mutierbare Anemic-Domain-Klasse zu einem invariantenreichen Domain Model. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
Musterloesung zu Modul B
Vergleiche deine Loesung nicht zeilenweise, sondern pruefe Architektur, Fehlerfaelle, Naming, Package-Regel und Testbarkeit.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | package com.seb4u.demo.domain; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.Currency; import java.util.Objects; public record Money(BigDecimal amount, Currency currency) implements Comparable<Money> { public Money { Objects.requireNonNull(amount, "amount"); Objects.requireNonNull(currency, "currency"); amount = amount.setScale(currency.getDefaultFractionDigits(), RoundingMode.HALF_UP); if (amount.signum() < 0) throw new IllegalArgumentException("Money must not be negative"); } public Money add(Money other) { requireSameCurrency(other); return new Money(amount.add(other.amount), currency); } public Money multiply(int quantity) { if (quantity < 0) throw new IllegalArgumentException("quantity must not be negative"); return new Money(amount.multiply(BigDecimal.valueOf(quantity)), currency); } private void requireSameCurrency(Money other) { if (!currency.equals(other.currency)) { throw new IllegalArgumentException("Currency mismatch: " + currency + " != " + other.currency); } } @Override public int compareTo(Money other) { requireSameCurrency(other); return amount.compareTo(other.amount); } } |
Workbook
Modul C - Runtime, I/O, NIO.2 und Concurrency
Du verarbeitest Dateien robust, vermeidest Path Traversal und steuerst Nebenläufigkeit mit Backpressure.
Aufgaben
- Atomarer CSV-Import: Lies eine große CSV streaming-basiert, schreibe das Ergebnis atomar in ein Zielverzeichnis und verschiebe fehlerhafte Dateien in quarantine/.
- Backpressure Pipeline: Baue Producer/Consumer mit begrenzter Queue, sauberer Cancellation und Fehleraggregation.
Review-Fragen
- Welche Invariante schützt diese Lösung?
- Welche Fehler sind fachlich, welche technisch?
- Welche Klasse kennt zu viel?
- Welche Tests würden einen Regression-Bug sofort zeigen?
Musterlösung anzeigen
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | package com.seb4u.demo.workbook.io; import java.io.*; import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.time.Instant; import java.util.*; public final class AtomicCsvImportSolution { private final Path inbox; private final Path processed; private final Path quarantine; public AtomicCsvImportSolution(Path root) throws IOException { this.inbox = root.resolve("inbox"); this.processed = root.resolve("processed"); this.quarantine = root.resolve("quarantine"); Files.createDirectories(inbox); Files.createDirectories(processed); Files.createDirectories(quarantine); } public ImportReport importFile(Path input) throws IOException { Path safeInput = input.normalize().toAbsolutePath(); if (!safeInput.startsWith(inbox.toAbsolutePath())) { throw new SecurityException("Path traversal attempt: " + input); } Path temp = Files.createTempFile(processed, "import-", ".tmp"); int lines = 0; try (BufferedReader reader = Files.newBufferedReader(safeInput, StandardCharsets.UTF_8); BufferedWriter writer = Files.newBufferedWriter(temp, StandardCharsets.UTF_8)) { String line; while ((line = reader.readLine()) != null) { if (line.isBlank()) continue; String[] columns = line.split(","); if (columns.length < 3) throw new IOException("Invalid CSV line: " + line); writer.write(columns[0].trim() + ";" + columns[1].trim() + ";" + columns[2].trim()); writer.newLine(); lines++; } } catch (Exception ex) { Files.move(safeInput, quarantine.resolve(safeInput.getFileName()), StandardCopyOption.REPLACE_EXISTING); Files.deleteIfExists(temp); throw ex; } Path target = processed.resolve(safeInput.getFileName().toString().replace(".csv", ".normalized.csv")); Files.move(temp, target, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE); return new ImportReport(target, lines, Instant.now()); } public record ImportReport(Path target, int importedLines, Instant finishedAt) {} } |
Zusätzliche Aufgaben und Musterlösungen
Diese Praxisaufgaben gehören direkt zu diesem Modul. Bearbeite sie nach den ersten Aufgaben und vergleiche anschließend deine Lösung mit der Musterlösung.
PraxisReviewMusterloesung
| # | Aufgabe | Abgabe | Prueffragen |
|---|---|---|---|
| C.1 | Implementiere einen sicheren Importer, der Path Traversal verhindert und atomar schreibt. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| C.2 | Baue einen File Watcher mit Debounce und Fehler-Quarantaene. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| C.3 | Verarbeite 10.000 Dateien mit Virtual Threads und begrenztem Bulkhead. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| C.4 | Erklaere, wann Memory-Mapped I/O sinnvoll ist und wann Streaming besser ist. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| C.5 | Implementiere kooperative Cancellation fuer lang laufende Dateiimporte. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
Musterloesung zu Modul C
Vergleiche deine Loesung nicht zeilenweise, sondern pruefe Architektur, Fehlerfaelle, Naming, Package-Regel und Testbarkeit.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | package com.seb4u.demo.io; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.UUID; public final class AtomicImportService { private final Path inbox; private final Path target; private final Path quarantine; public AtomicImportService(Path inbox, Path target, Path quarantine) { this.inbox = inbox.toAbsolutePath().normalize(); this.target = target.toAbsolutePath().normalize(); this.quarantine = quarantine.toAbsolutePath().normalize(); } public Path importFile(String submittedName) throws IOException { Path source = inbox.resolve(submittedName).normalize(); if (!source.startsWith(inbox)) throw new SecurityException("Path traversal blocked"); if (!Files.isRegularFile(source)) throw new IOException("Not a file: " + source); Path temp = target.resolve("." + UUID.randomUUID() + ".tmp"); Path finalFile = target.resolve(source.getFileName().toString()).normalize(); if (!finalFile.startsWith(target)) throw new SecurityException("Target traversal blocked"); try { Files.createDirectories(target); Files.copy(source, temp, StandardCopyOption.REPLACE_EXISTING); Files.move(temp, finalFile, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING); return finalFile; } catch (Exception ex) { Files.createDirectories(quarantine); Files.move(source, quarantine.resolve(source.getFileName()), StandardCopyOption.REPLACE_EXISTING); throw ex; } finally { Files.deleteIfExists(temp); } } } |
Workbook
Modul D - Professional Backend und Architektur
Du trennst Domain, Use Cases und Adapter sauber und prüfst Architekturgrenzen.
Aufgaben
- Port zuerst: Definiere einen Port für Zahlungsfreigabe und implementiere einen Fake Adapter.
- Use-Case transaktional denken: Baue einen Use Case, der validiert, speichert und ein Outbox-Event erzeugt.
Review-Fragen
- Welche Invariante schützt diese Lösung?
- Welche Fehler sind fachlich, welche technisch?
- Welche Klasse kennt zu viel?
- Welche Tests würden einen Regression-Bug sofort zeigen?
Musterlösung anzeigen
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | package com.seb4u.demo.workbook.architecture; import java.time.Instant; import java.util.UUID; public final class PlaceOrderWorkflowSolution { public Result<OrderId> place(PlaceOrderCommand command, OrderRepository repository, PaymentPort payment, OutboxPort outbox) { if (command.customerId().isBlank() || command.totalCents() <= 0) { return Result.error("Invalid order command"); } OrderId id = new OrderId(UUID.randomUUID().toString()); PaymentDecision decision = payment.authorize(command.customerId(), command.totalCents()); if (!decision.approved()) return Result.error("Payment rejected: " + decision.reason()); repository.save(new Order(id, command.customerId(), command.totalCents(), Instant.now())); outbox.publish("OrderPlaced", id.value()); return Result.ok(id); } public record PlaceOrderCommand(String customerId, long totalCents) {} public record OrderId(String value) {} public record Order(OrderId id, String customerId, long totalCents, Instant placedAt) {} public record PaymentDecision(boolean approved, String reason) {} public interface OrderRepository { void save(Order order); } public interface PaymentPort { PaymentDecision authorize(String customerId, long totalCents); } public interface OutboxPort { void publish(String type, String aggregateId); } public sealed interface Result<T> { record Ok<T>(T value) implements Result<T> {} record Error<T>(String message) implements Result<T> {} static <T> Result<T> ok(T value) { return new Ok<>(value); } static <T> Result<T> error(String message) { return new Error<>(message); } } } |
Zusätzliche Aufgaben und Musterlösungen
Diese Praxisaufgaben gehören direkt zu diesem Modul. Bearbeite sie nach den ersten Aufgaben und vergleiche anschließend deine Lösung mit der Musterlösung.
PraxisReviewMusterloesung
| # | Aufgabe | Abgabe | Prueffragen |
|---|---|---|---|
| D.1 | Trenne Domain, Use Case und Adapter fuer einen Zahlungsprozess. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| D.2 | Erstelle ein Result-Pattern fuer erfolgreiche und fehlgeschlagene Use Cases. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| D.3 | Baue einen Port fuer Zeit/ID-Erzeugung, damit Tests deterministisch sind. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| D.4 | Dokumentiere Architekturregeln: Domain kennt keine Spring-, Jakarta- oder SQL-Klassen. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| D.5 | Implementiere ein Transaction Script und refactore es zu einem Use Case. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
Musterloesung zu Modul D
Vergleiche deine Loesung nicht zeilenweise, sondern pruefe Architektur, Fehlerfaelle, Naming, Package-Regel und Testbarkeit.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | package com.seb4u.demo.architecture; import java.time.Instant; import java.util.UUID; public final class PlaceOrderUseCase { private final OrderRepository orders; private final PaymentPort payments; private final EventPublisher events; private final ClockPort clock; public PlaceOrderUseCase(OrderRepository orders, PaymentPort payments, EventPublisher events, ClockPort clock) { this.orders = orders; this.payments = payments; this.events = events; this.clock = clock; } public Result<OrderReceipt> handle(PlaceOrderCommand command) { var validation = command.validate(); if (!validation.ok()) return Result.invalid(validation.message()); Order order = Order.create(UUID.randomUUID(), command.lines(), clock.now()); PaymentResult payment = payments.authorize(order.total()); if (!payment.approved()) return Result.rejected("Payment rejected: " + payment.reason()); orders.save(order); events.publish(new OrderPlaced(order.id(), order.total(), clock.now())); return Result.ok(new OrderReceipt(order.id(), order.total())); } public interface ClockPort { Instant now(); } public interface OrderRepository { void save(Order order); } public interface PaymentPort { PaymentResult authorize(Money total); } public interface EventPublisher { void publish(Object event); } } |
Workbook
Modul E - CommerceFlow Master-Projekt
Du nutzt das Projektpaket als realistische Referenz für Domain, Application Layer, Adapter und Tests.
Aufgaben
- Domain-Regel ergänzen: Order darf nur bezahlt werden, wenn sie CREATED ist.
- Outbox idempotent machen: Verhindere doppelte Events bei Wiederholung desselben Kommandos.
Review-Fragen
- Welche Invariante schützt diese Lösung?
- Welche Fehler sind fachlich, welche technisch?
- Welche Klasse kennt zu viel?
- Welche Tests würden einen Regression-Bug sofort zeigen?
Musterlösung anzeigen
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | package com.seb4u.demo.workbook.commerceflow; import java.util.*; import java.util.concurrent.ConcurrentHashMap; public final class IdempotentOutboxSolution { private final Map<String, OutboxEvent> eventsByKey = new ConcurrentHashMap<>(); public boolean publishOnce(String idempotencyKey, String type, String aggregateId, String payload) { OutboxEvent event = new OutboxEvent(UUID.randomUUID().toString(), type, aggregateId, payload); return eventsByKey.putIfAbsent(idempotencyKey, event) == null; } public List<OutboxEvent> pending() { return List.copyOf(eventsByKey.values()); } public record OutboxEvent(String id, String type, String aggregateId, String payload) {} } |
Zusätzliche Aufgaben und Musterlösungen
Diese Praxisaufgaben gehören direkt zu diesem Modul. Bearbeite sie nach den ersten Aufgaben und vergleiche anschließend deine Lösung mit der Musterlösung.
PraxisReviewMusterloesung
| # | Aufgabe | Abgabe | Prueffragen |
|---|---|---|---|
| E.1 | Erweitere CommerceFlow um OrderStatus: DRAFT, PLACED, PAID, SHIPPED, CANCELLED. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| E.2 | Baue eine Outbox-Tabelle oder Datei und erklaere, warum Events nicht direkt gesendet werden. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| E.3 | Implementiere Idempotenz fuer PlaceOrderCommand mit Idempotency-Key. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| E.4 | Schreibe eine Projection fuer Tagesumsatz und Anzahl bezahlter Bestellungen. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| E.5 | Erstelle einen Replay-Plan: Welche Events sind stabil, welche muessen migriert werden? | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
Musterloesung zu Modul E
Vergleiche deine Loesung nicht zeilenweise, sondern pruefe Architektur, Fehlerfaelle, Naming, Package-Regel und Testbarkeit.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | package com.seb4u.demo.commerceflow.application; import java.time.Instant; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public final class IdempotencyService { private final Map<String, StoredResponse> responses = new ConcurrentHashMap<>(); public StoredResponse executeOnce(String key, RequestFingerprint fingerprint, Operation operation) { return responses.compute(key, (ignored, existing) -> { if (existing != null && !existing.fingerprint().equals(fingerprint)) { throw new IllegalStateException("Idempotency key reused with different request"); } if (existing != null) return existing; return new StoredResponse(fingerprint, operation.run(), Instant.now()); }); } public record RequestFingerprint(String method, String path, String bodyHash) {} public record StoredResponse(RequestFingerprint fingerprint, String body, Instant createdAt) {} public interface Operation { String run(); } } |
Workbook
Modul F - Enterprise Java: Spring, Persistence, Testing, Security
Du baust produktionsnahe APIs mit Validierung, Fehlerverträgen, Tests und Security-Grundlagen.
Aufgaben
- Fehlervertrag: Baue eine Spring-Exception-Handler-Struktur mit Problem-Details.
- Repository-Grenze: Verhindere, dass JPA Entities in Domain-Use-Cases durchsickern.
Review-Fragen
- Welche Invariante schützt diese Lösung?
- Welche Fehler sind fachlich, welche technisch?
- Welche Klasse kennt zu viel?
- Welche Tests würden einen Regression-Bug sofort zeigen?
Musterlösung anzeigen
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | package com.seb4u.demo.spring.workbook; import org.springframework.http.HttpStatus; import org.springframework.http.ProblemDetail; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice public final class ApiExceptionHandlerSolution { @ExceptionHandler(MethodArgumentNotValidException.class) ProblemDetail validation(MethodArgumentNotValidException ex) { ProblemDetail detail = ProblemDetail.forStatus(HttpStatus.BAD_REQUEST); detail.setTitle("Validation failed"); detail.setDetail("Request body violates the API contract"); detail.setProperty("fieldErrors", ex.getBindingResult().getFieldErrors().stream() .map(err -> err.getField() + ": " + err.getDefaultMessage()) .toList()); return detail; } @ExceptionHandler(IllegalArgumentException.class) ProblemDetail badRequest(IllegalArgumentException ex) { ProblemDetail detail = ProblemDetail.forStatus(HttpStatus.BAD_REQUEST); detail.setTitle("Invalid request"); detail.setDetail(ex.getMessage()); return detail; } } |
Zusätzliche Aufgaben und Musterlösungen
Diese Praxisaufgaben gehören direkt zu diesem Modul. Bearbeite sie nach den ersten Aufgaben und vergleiche anschließend deine Lösung mit der Musterlösung.
PraxisReviewMusterloesung
| # | Aufgabe | Abgabe | Prueffragen |
|---|---|---|---|
| F.1 | Baue eine REST-Fehlerantwort mit code, message, traceId und fieldErrors. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| F.2 | Implementiere einen Spring Controller, der DTOs validiert und Domain-Fehler mappt. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| F.3 | Schreibe einen Repository-Test mit Testdatenaufbau und Rollback-Erwartung. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| F.4 | Vergleiche JDBC, JPA und jOOQ fuer CommerceFlow-Persistence. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| F.5 | Baue eine Security-Checkliste fuer Auth, Rollen, Logging und Secrets. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
Musterloesung zu Modul F
Vergleiche deine Loesung nicht zeilenweise, sondern pruefe Architektur, Fehlerfaelle, Naming, Package-Regel und Testbarkeit.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | package com.seb4u.demo.spring.api; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/orders") public final class OrderResource { private final PlaceOrderService service; public OrderResource(PlaceOrderService service) { this.service = service; } @PostMapping ResponseEntity<?> place(@Valid @RequestBody PlaceOrderRequest request, @RequestHeader("Idempotency-Key") @NotBlank String key) { return switch (service.place(request.toCommand(key))) { case PlaceOrderService.Result.Created created -> ResponseEntity.status(201).body(created.body()); case PlaceOrderService.Result.Invalid invalid -> ResponseEntity.badRequest().body(invalid.problem()); case PlaceOrderService.Result.Conflict conflict -> ResponseEntity.status(409).body(conflict.problem()); }; } public record PlaceOrderRequest(String customerId, java.util.List<String> productIds) { PlaceOrderCommand toCommand(String key) { return new PlaceOrderCommand(key, customerId, productIds); } } } |
Workbook
Modul G - Job-Ready, Code Review und Interview
Du übst erklären, refactoren, begründen und typische Senior-Fallen erkennen.
Aufgaben
- Code Review: Finde Race Conditions in einem gemeinsam genutzten HashMap-Cache.
- Interview Antwort: Erkläre Unterschied zwischen checked Exception, RuntimeException und Fehlervertrag einer API.
Review-Fragen
- Welche Invariante schützt diese Lösung?
- Welche Fehler sind fachlich, welche technisch?
- Welche Klasse kennt zu viel?
- Welche Tests würden einen Regression-Bug sofort zeigen?
Musterlösung anzeigen
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | package com.seb4u.demo.workbook.interview; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; public final class ThreadSafeMemoizerSolution<K, V> { private final ConcurrentHashMap<K, V> cache = new ConcurrentHashMap<>(); public V getOrCompute(K key, Supplier<V> supplier) { return cache.computeIfAbsent(key, ignored -> supplier.get()); } public int size() { return cache.size(); } } |
Zusätzliche Aufgaben und Musterlösungen
Diese Praxisaufgaben gehören direkt zu diesem Modul. Bearbeite sie nach den ersten Aufgaben und vergleiche anschließend deine Lösung mit der Musterlösung.
PraxisReviewMusterloesung
| # | Aufgabe | Abgabe | Prueffragen |
|---|---|---|---|
| G.1 | Analysiere einen Codeblock auf Race Conditions, Ressourcenlecks und falsche Exceptions. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| G.2 | Bereite eine 3-Minuten-Erklaerung zu Virtual Threads vs Thread Pools vor. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| G.3 | Schreibe eine Senior-Antwort zu: Warum Hexagonal Architecture? | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| G.4 | Refactore einen God Service in Use Cases und Ports. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| G.5 | Erstelle ein Code-Review-Protokoll mit Befund, Risiko, Vorschlag und Test. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
Musterloesung zu Modul G
Vergleiche deine Loesung nicht zeilenweise, sondern pruefe Architektur, Fehlerfaelle, Naming, Package-Regel und Testbarkeit.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | package com.seb4u.demo.review; import java.util.ArrayList; import java.util.List; public final class ReviewReport { private final List<Finding> findings = new ArrayList<>(); public ReviewReport add(Severity severity, String location, String risk, String recommendation) { findings.add(new Finding(severity, location, risk, recommendation)); return this; } public boolean blocksMerge() { return findings.stream().anyMatch(f -> f.severity == Severity.CRITICAL || f.severity == Severity.HIGH); } public String markdown() { StringBuilder out = new StringBuilder("# Code Review Report "); for (Finding f : findings) { out.append("- **").append(f.severity).append("** `").append(f.location).append("`: ") .append(f.risk).append(" -> ").append(f.recommendation).append(' '); } return out.toString(); } enum Severity { LOW, MEDIUM, HIGH, CRITICAL } record Finding(Severity severity, String location, String risk, String recommendation) {} } |
Workbook
Modul H - Fullstack Java
Du verbindest Backend-API, Validierung, UI-Flows, Uploads und Exporte.
Aufgaben
- Pagination Contract: Definiere eine PageResponse<T> mit page, size, totalElements und items.
- Upload Flow: Prüfe Dateiname, Größe, MIME-Typ und Zielpfad, bevor gespeichert wird.
Review-Fragen
- Welche Invariante schützt diese Lösung?
- Welche Fehler sind fachlich, welche technisch?
- Welche Klasse kennt zu viel?
- Welche Tests würden einen Regression-Bug sofort zeigen?
Musterlösung anzeigen
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | package com.seb4u.demo.workbook.fullstack; import java.util.List; public record PageResponse<T>( int page, int size, long totalElements, List<T> items ) { public PageResponse { if (page < 0) throw new IllegalArgumentException("page must be >= 0"); if (size <= 0 || size > 200) throw new IllegalArgumentException("size must be 1..200"); if (totalElements < 0) throw new IllegalArgumentException("totalElements must be >= 0"); items = List.copyOf(items); } public long totalPages() { return Math.max(1, (long) Math.ceil((double) totalElements / size)); } } |
Zusätzliche Aufgaben und Musterlösungen
Diese Praxisaufgaben gehören direkt zu diesem Modul. Bearbeite sie nach den ersten Aufgaben und vergleiche anschließend deine Lösung mit der Musterlösung.
PraxisReviewMusterloesung
| # | Aufgabe | Abgabe | Prueffragen |
|---|---|---|---|
| H.1 | Entwerfe einen Login-/Register-Flow mit Backend-Fehlern und UI-Mapping. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| H.2 | Implementiere Pagination, Sorting und Filtering fuer eine Order-Tabelle. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| H.3 | Baue einen Upload-Flow mit MIME-Pruefung und Groessenlimit. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| H.4 | Dokumentiere Client-seitige und Server-seitige Validierung gemeinsam. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| H.5 | Erstelle eine Fehlerseite fuer 404, 409 und 500 mit Support-Code. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
Musterloesung zu Modul H
Vergleiche deine Loesung nicht zeilenweise, sondern pruefe Architektur, Fehlerfaelle, Naming, Package-Regel und Testbarkeit.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | package com.seb4u.demo.spring.fullstack; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; public final class PageSpecFactory { private static final int MAX_SIZE = 100; public PageRequest from(int page, int size, String sortBy, String direction) { int safePage = Math.max(page, 0); int safeSize = Math.min(Math.max(size, 1), MAX_SIZE); String property = switch (sortBy == null ? "createdAt" : sortBy) { case "createdAt", "status", "total" -> sortBy == null ? "createdAt" : sortBy; default -> "createdAt"; }; Sort.Direction dir = "asc".equalsIgnoreCase(direction) ? Sort.Direction.ASC : Sort.Direction.DESC; return PageRequest.of(safePage, safeSize, Sort.by(dir, property)); } } |
Workbook
Modul I - Microservices, Messaging und Cloud
Du entwirfst Services mit Idempotenz, Outbox, Retry, Circuit Breaker und Observability.
Aufgaben
- Idempotency Store: Baue einen Store, der Request-Keys mit Response-Snapshots verbindet.
- Retry Policy: Unterscheide transient, permanent und poison message.
Review-Fragen
- Welche Invariante schützt diese Lösung?
- Welche Fehler sind fachlich, welche technisch?
- Welche Klasse kennt zu viel?
- Welche Tests würden einen Regression-Bug sofort zeigen?
Musterlösung anzeigen
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | package com.seb4u.demo.workbook.cloud; import java.time.Duration; import java.time.Instant; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; public final class IdempotencyStoreSolution { private final ConcurrentHashMap<String, Entry> entries = new ConcurrentHashMap<>(); private final Duration ttl; public IdempotencyStoreSolution(Duration ttl) { this.ttl = ttl; } public Optional<String> getValidResponse(String key) { Entry entry = entries.get(key); if (entry == null) return Optional.empty(); if (entry.createdAt().plus(ttl).isBefore(Instant.now())) { entries.remove(key, entry); return Optional.empty(); } return Optional.of(entry.responseBody()); } public boolean save(String key, String responseBody) { return entries.putIfAbsent(key, new Entry(responseBody, Instant.now())) == null; } private record Entry(String responseBody, Instant createdAt) {} } |
Zusätzliche Aufgaben und Musterlösungen
Diese Praxisaufgaben gehören direkt zu diesem Modul. Bearbeite sie nach den ersten Aufgaben und vergleiche anschließend deine Lösung mit der Musterlösung.
PraxisReviewMusterloesung
| # | Aufgabe | Abgabe | Prueffragen |
|---|---|---|---|
| I.1 | Erklaere, warum Exactly Once meist ein Systemziel und kein Broker-Schalter ist. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| I.2 | Implementiere Consumer-Idempotenz mit processed_message Tabelle oder In-Memory-Store. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| I.3 | Baue eine Saga fuer Payment -> Inventory -> Shipping mit Kompensationen. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| I.4 | Definiere Readiness- und Liveness-Probes fuer CommerceFlow. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| I.5 | Schreibe eine Entscheidungsmatrix: REST, Events, Batch oder Streaming. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
Musterloesung zu Modul I
Vergleiche deine Loesung nicht zeilenweise, sondern pruefe Architektur, Fehlerfaelle, Naming, Package-Regel und Testbarkeit.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | package com.seb4u.demo.cloud; import java.time.Instant; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; public final class IdempotentConsumer { private final Set<String> processedIds = ConcurrentHashMap.newKeySet(); public ConsumptionResult consume(Message message, MessageHandler handler) { if (!processedIds.add(message.id())) { return new ConsumptionResult(message.id(), "duplicate-skipped", Instant.now()); } try { handler.handle(message); return new ConsumptionResult(message.id(), "processed", Instant.now()); } catch (RuntimeException ex) { processedIds.remove(message.id()); throw ex; } } public record Message(String id, String type, String payload) {} public record ConsumptionResult(String id, String status, Instant at) {} public interface MessageHandler { void handle(Message message); } } |
Workbook
Modul J - Algorithmen und Datenstrukturen
Du implementierst klassische Interview-Algorithmen sauber, generisch und testbar in Java.
Aufgaben
- Dijkstra: Implementiere kürzeste Wege mit PriorityQueue.
- Sliding Window: Finde das längste Teilarray mit Summe <= Limit.
Review-Fragen
- Welche Invariante schützt diese Lösung?
- Welche Fehler sind fachlich, welche technisch?
- Welche Klasse kennt zu viel?
- Welche Tests würden einen Regression-Bug sofort zeigen?
Musterlösung anzeigen
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | package com.seb4u.demo.workbook.algorithms; import java.util.*; public final class DijkstraSolution { public Map<String, Integer> shortestPaths(Map<String, List<Edge>> graph, String start) { Map<String, Integer> distance = new HashMap<>(); PriorityQueue<Node> queue = new PriorityQueue<>(Comparator.comparingInt(Node::distance)); distance.put(start, 0); queue.add(new Node(start, 0)); while (!queue.isEmpty()) { Node current = queue.poll(); if (current.distance() != distance.getOrDefault(current.name(), Integer.MAX_VALUE)) continue; for (Edge edge : graph.getOrDefault(current.name(), List.of())) { int nextDistance = current.distance() + edge.weight(); if (nextDistance < distance.getOrDefault(edge.to(), Integer.MAX_VALUE)) { distance.put(edge.to(), nextDistance); queue.add(new Node(edge.to(), nextDistance)); } } } return Map.copyOf(distance); } public record Edge(String to, int weight) { public Edge { if (weight < 0) throw new IllegalArgumentException("Dijkstra requires non-negative weights"); } } private record Node(String name, int distance) {} } |
Zusätzliche Aufgaben und Musterlösungen
Diese Praxisaufgaben gehören direkt zu diesem Modul. Bearbeite sie nach den ersten Aufgaben und vergleiche anschließend deine Lösung mit der Musterlösung.
PraxisReviewMusterloesung
| # | Aufgabe | Abgabe | Prueffragen |
|---|---|---|---|
| J.1 | Implementiere LRU Cache mit LinkedHashMap und erklaere die Komplexitaet. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| J.2 | Loese eine Sliding-Window-Aufgabe fuer laengste Teilfolge ohne Duplikate. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| J.3 | Implementiere Dijkstra mit PriorityQueue und Map. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| J.4 | Baue Union-Find mit Path Compression und Union by Rank. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| J.5 | Vergleiche DFS und BFS an einem konkreten Graphproblem. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
Musterloesung zu Modul J
Vergleiche deine Loesung nicht zeilenweise, sondern pruefe Architektur, Fehlerfaelle, Naming, Package-Regel und Testbarkeit.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | package com.seb4u.demo.algorithms; import java.util.HashMap; import java.util.Map; public final class SlidingWindow { public int longestUniqueSubstring(String text) { Map<Character, Integer> lastSeen = new HashMap<>(); int left = 0; int best = 0; for (int right = 0; right < text.length(); right++) { char c = text.charAt(right); Integer previous = lastSeen.put(c, right); if (previous != null && previous >= left) { left = previous + 1; } best = Math.max(best, right - left + 1); } return best; } } |
Workbook
Modul K - Abschluss, Portfolio und Wiederholung
Du fasst dein Wissen in einem Portfolio zusammen und wiederholst mit Cheat-Sheets.
Aufgaben
- Portfolio README: Schreibe eine README mit Architekturentscheidung, Run-Anleitung, Teststrategie und bekannten Trade-offs.
- 30-Minuten Wiederholung: Nutze die Cheat-Sheets, um ein Thema laut zu erklären und danach ein Mini-Beispiel zu schreiben.
Review-Fragen
- Welche Invariante schützt diese Lösung?
- Welche Fehler sind fachlich, welche technisch?
- Welche Klasse kennt zu viel?
- Welche Tests würden einen Regression-Bug sofort zeigen?
Musterlösung anzeigen
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | package com.seb4u.demo.workbook.portfolio; public final class PortfolioReadmeTemplate { public static String render(String projectName) { return """ # %s ## Ziel Kurze fachliche Beschreibung und Lernziel. ## Architektur - Domain ohne Framework-Abhängigkeit - Application Layer mit Ports - Adapter für File, Spring oder Jakarta ## Start ```bash mvn test mvn -pl commerceflow-adapter-file exec:java ``` ## Tests Unit Tests für Domain, Integration Tests für Adapter, Acceptance Tests für Hauptflows. ## Trade-offs Bewusste Vereinfachungen, technische Schulden und nächste Verbesserungen. """.formatted(projectName); } } |
Zusätzliche Aufgaben und Musterlösungen
Diese Praxisaufgaben gehören direkt zu diesem Modul. Bearbeite sie nach den ersten Aufgaben und vergleiche anschließend deine Lösung mit der Musterlösung.
PraxisReviewMusterloesung
| # | Aufgabe | Abgabe | Prueffragen |
|---|---|---|---|
| K.1 | Baue eine Portfolio-Seite mit Problem, Architektur, Trade-offs, Tests und Screenshots. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| K.2 | Schreibe ein finales Runbook: Build, Test, Start, Smoke-Test, Troubleshooting. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| K.3 | Erstelle eine Lernmatrix: Was kann ich erklaeren, implementieren, testen und betreiben? | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| K.4 | Fuehre ein Mock-Interview mit 10 Fragen aus Core, Concurrency, Spring und Architektur durch. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
| K.5 | Plane eine 30-Tage-Wiederholung mit Cheat-Sheets, Assessment und Projektaufgaben. | Code + kurze Begruendung | Ist es testbar? Sind Fehlerfaelle klar? Passt das Package? |
Musterloesung zu Modul K
Vergleiche deine Loesung nicht zeilenweise, sondern pruefe Architektur, Fehlerfaelle, Naming, Package-Regel und Testbarkeit.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | package com.seb4u.demo.portfolio; import java.util.List; public record PortfolioEvidence( String title, String problem, String architectureDecision, List<String> tests, List<String> productionConcerns, String demoCommand) { public String asMarkdown() { return """ # %s ## Problem %s ## Architekturentscheidung %s ## Tests %s ## Production Concerns %s ## Demo ```bash %s ``` """.formatted( title, problem, architectureDecision, String.join(" ", tests.stream().map(t -> "- " + t).toList()), String.join(" ", productionConcerns.stream().map(c -> "- " + c).toList()), demoCommand); } } |