Entwicklung & Code
Deep Dive Spring Modulith Teil 2: Event-Mechanismen und Teststrategien
Events sind in Spring Modulith ein zentrales Mittel zur losen Kopplung zwischen Modulen. Sie lassen sich zeitgesteuert auslösen, an externe Systeme weiterreichen und gezielt testen. Nachdem im ersten Teil die fachliche Zerlegung einer Spring Boot-Anwendung in klar abgegrenzte Module im Mittelpunkt stand, geht es nun um weitere Event-Mechanismen sowie um Teststrategien, mit denen sich einzelne Module isoliert und realitätsnah überprüfen lassen.
Weiterlesen nach der Anzeige

Nils Hartmann ist freiberuflicher Softwareentwickler und Coach mit den Schwerpunkten Java im Backend und React im Frontend, wozu er auch Workshops und Trainings gibt.
Wenn die Zeit vergeht – zeitgesteuerte Events
Das Beispiel des Portals zur Pflanzenpflege hat bislang Events demonstriert, die Aktionen durchführen, nachdem eine fachliche Verarbeitung („Pflanze registriert“) erfolgt ist. Daneben gibt es oft auch Aktionen, die zeitgesteuert ausgeführt werden sollen. Zum Beispiel sollen jeden Monat Rechnungen für alle Kunden erzeugt und verschickt werden. Zeitgesteuerte Aktionen lassen sich in Spring Boot mit der Annotation @Scheduled umsetzen. Dieser wird beispielsweise ein cron-Ausdruck übergeben, der festlegt, wann die annotierte Methode ausgeführt wird. Solche Methoden lassen sich jedoch schwer testen. Im beschriebenen Beispiel müsste der Test zum Erzeugen von Rechnungen die Zeit künstlich setzen, um dafür zu sorgen, dass Spring die zeitgesteuerte Methode aufruft. Zudem ist ein cron-Ausdruck weniger ausdrucksstark, da er Zeitpunkte („jeweils am Monatsersten um 3 Uhr“) aber keine Ereignisse („Monatswechsel ist erfolgt“) angibt.
Mit der Moments-API, als Bestandteil von Spring Modulith, gibt es einen weiteren Weg, wiederkehrende Aktionen auszuführen. Sie besteht aus einer Reihe von Events, die Spring Modulith am Ende eines Zeitraums (z. B. Stunden-, Tages- oder Quartalswechsel) auslöst und die von der Anwendung konsumiert werden können. So lässt sich beispielsweise beim Monatswechsel auf das Event MonthHasPassed reagieren. Die Verarbeitung erfolgt wie bei regulären Events, in einer mit @EventListener annotierten Methode, die als Argument den gewünschten Event-Typen entgegennimmt. Listing 1 zeigt die Verwendung exemplarisch für den InvoiceGenerator, der beim Monatswechsel die Rechnungen erzeugt.
Listing 1: Der InoviceGenerator wird beim Monatswechsel über das MomentHasPassed-Event getriggert
package nh.demo.plantify.billing.invoice;
import org.springframework.context.event.EventListener;
import org.springframework.modulith.moments.MonthHasPassed;
// ...
@Component
class InvoiceGenerator {
// ...
@EventListener
@Transactional
void generateInvoices(MonthHasPassed event) {
var month = event.getMonth();
// Rechnungen für den Monat month erzeugen
// ...
}
}
Vorteil an dieser Variante ist, dass der Code mit den Moments Events nicht nur verständlicher ist, als bei der Verwendung von @Scheduler, die Events liefern auch nützliche Informationen (z. B. welcher Monat abgelaufen ist). Das Bean TimeMaschine, das Spring Modulith automatisch im Application Context registriert, löst die Events aus. Für Tests lässt sich deren Zeit künstlich weiterstellen. Alle Events, die normalerweise in diesem vorgespulten Zeitraum ausgelöst würden, werden damit auch im Test ausgelöst.
Weiterlesen nach der Anzeige
Events für externe Systeme veröffentlichen
Die Anwendung nutzt den ApplicationEventPublisher, um fachliche Events innerhalb der Anwendung zu veröffentlichen. Mit Spring Modulith lassen sich darüber Events auch an externe Systeme wie Kafka, RabbitMQ oder einen JMS Server übergeben. Dafür stellt Spring Modulith Broker-spezifische Starter-Module zur Verfügung, die sich über die Maven- oder Gradle-Konfiguration der Anwendung einbinden lassen.
Die Annotation @Externalized an einer Event-Klasse drückt aus, dass das Event an ein externes System geschickt werden soll. Listing 2 zeigt das InvoiceCreatedEvent, das die Anwendung an Kafka schickt. Das target-Attribute enthält einen Broker-spezifischen Ausdruck, der angibt, wie das Event geroutet werden soll. Im Beispiel wird das Event an das Kafka-Topic invoices geschickt, wobei die im Event enthaltene ownerId den Message Key bildet.
Listing 2: Ein Application Event, das über Kafka veröffentlicht wird
package nh.demo.plantify.billing.invoice;
import org.springframework.modulith.events.Externalized;
// ...
@Externalized(target = "invoices::#{#this.ownerId()}")
public record InvoiceGeneratedEvent(
UUID ownerId,
YearMonth billingPeriod,
BigDecimal amount
) { }
In der kommenden Version 2.1 implementiert Spring Modulith das Outbox-Pattern für das Versenden von @Externalized-Events. Damit stellt es sicher, dass jedes Event mindestens einmal erfolgreich an das externe System übermittelt wurde. Dazu wird intern das Framework Namastack verwendet, das eine Implementierung des Outbox-Patterns für Spring Boot-Anwendungen bereitstellt. Um das Verhalten für die eigene Anwendung zu aktivieren, muss lediglich eine Spring Property gesetzt werden. Im Rahmen der Ankündigung dieses Features hat sich eine interessante Diskussion über die konkrete Implementierung durch Namastack und deren Konsequenzen zwischen Roland Beisel (Lead von Namastack Outbox) und Gunnar Morling (ehem. Tech-Lead von Debezium) entwickelt.
Eine Anwendung kann im Rahmen einer fachlichen Verarbeitung sowohl Daten in der eigenen Datenbank speichern als auch ein Event an ein externes System schicken (@Externalized). Allerdings ist der Versand eines externen Events nicht Bestandteil der Datenbank-Transaktion. Wenn die Transaktion also erfolgreich committed wurde, dann aber das Event nicht zugestellt werden kann (z. B. weil es Verbindungsprobleme zum Message Broker gibt), ist das Event verloren. Im Beispiel hätte die Anwendung eine Rechnung erzeugt und gespeichert, die aber nicht per Event zugestellt wurde.
Auch wenn man die Reihenfolge umdrehen würde, also erst auf die erfolgreiche Abgabe des Events wartet und die Anwendung danach die Transaktion committed, kann es zu einer Inkonsistenz kommen. In dieser Konstellation ist es möglich, dass das Event an das externe System abgegeben wird, danach die Anwendung aber ihre Transaktion nicht committen kann. Dann kann Spring das verschickte Event nicht zurückholen. In der Beispiel-Anwendung bedeutet dass, dass das externe System eine Rechnung erhält, die in der Anwendung gar nicht bekannt ist.
Um dieses Problem zu lösen, kann eine Anwendung das Outbox-Pattern verwenden. Dann sendet die Anwendung das Event nicht sofort, sondern speichert das Event beim Commit der Transaktion zunächst gemeinsam mit den anderen Daten in der Datenbank in einer eigenen Tabelle. Diese Tabelle dient als Postausgangskorb („Outbox“). Erst danach versucht die Anwendung in einer neuen Transaktion, das Event aus der Outbox-Tabelle zuzustellen. Wenn dabei ein Fehler auftritt, verbleibt das Event in der Tabelle, sodass die Anwendung später erneut dessen Zustellung versuchen kann. Sobald die Anwendung das Event erfolgreich versenden konnte, entfernt sie es aus der Datenbank und schließt die Transaktion ab. Kommt es dabei zu einem Fehler, sodass die Transaktion nicht committed werden kann, verbleibt das Event in der Outbox-Tabelle, obwohl es zuvor erfolgreich an das externe System gesendet wurde. Da das Event weiterhin in der Outbox-Tabelle liegt, wird die Anwendung beim nächsten Durchlauf erneut versuchen, es zu versenden. Dadurch erhält der Empfänger das Event zwar möglicherweise mehr als einmal, aber auf diese Weise kann die Anwendung garantieren, dass es mindestens einmal zugestellt wird und nicht gänzlich verloren geht („At least once“-Semantik). Der Empfänger muss also damit rechnen, ein Event mehrfach zu erhalten und bei der Verarbeitung doppelte Events beispielsweise ignorieren (Idempotenz). Dabei handelt es sich um ein typisches Muster in verteilten Systemen, in denen echte systemübergreifende Transaktionen nicht möglich oder nur sehr aufwendig sind.
Modularisierte Datenbankmigrationen
Bis hierhin lag der Fokus auf der Aufteilung des Java-Codes in abgeschlossene Application Modules. Oft gehören zu einer Anwendung aber auch Migrationsskripte für die Datenbank, die beispielsweise Flyway verwaltet. Spring Boot führt diese beim Starten der Anwendung automatisch aus und sorgt dafür, dass die Datenbank auf einem für die aktuelle Version der Anwendung passenden Stand ist. Typischerweise finden sich die Flyway-Skripte einer Anwendung in einem zentralen migration-Verzeichnis als Dateien mit einer Versionsnummer im Namen. Die zentrale Verwaltung und Versionierung der Migrationsskripte widersprechen allerdings der Idee der Modularisierung.
Aus diesem Grund bietet Spring Modulith die Möglichkeit, die Skripte nicht nur zentral, sondern pro Modul abzulegen und zu versionieren. So kann jedes Modul seine eigene Historie von Migrationsskripten pflegen. Die übergeordnete Reihenfolge ergibt sich aus den Modulabhängigkeiten. Die modulspezifischen Flyway-Dateien gilt es in einem Unterordner abzulegen, der genauso wie das entsprechende Modul heißt. Zusätzlich kommt das Verzeichnis __root zum Einsatz, in dem globale Migrationsskripte ihren Platz finden. Die Anwendung führt die Skripte in diesem Verzeichnis immer als Erstes aus. Abbildung 1 zeigt die Migrationsskripte von Plantify.

Modularisierte Flyway Migrationsskripte für die Datenbank (Abb. 1)
(Bild: Nils Hartmann)
Damit die Modulstruktur auch in der Datenbank eingehalten wird, definiert jedes Modul sein eigenes Datenbankschema in Postgres. Im __root-Ordner befinden sich die Skripte zum Anlegen der Tabellen für die Event Publication Registry. In einem Integrationstest führt Spring Modulith immer nur die Skripte aus, deren Modul gerade getestet wird, und stellt damit sicher, dass es keine unerwünschten Referenzen zwischen den Skripten gibt.
Entwicklung & Code
Apples früherer KI-Chef John Giannandrea verlässt das Unternehmen
Es war ein denkwürdiger Auftritt am Nachmittag des 11. Juni 2024: Apples damaliger KI-Chef John Giannandrea und Software-Chef Craig Federighi demonstrierten auf der Bühne des Steve Jobs Theater im Apple-Hauptquartier in Cupertino den großen Schulterschluss. Die von Apple-Chef Tim Cook eingeleitete Fragestunde mit YouTuberin iJustine sollte die kurz zuvor vorgestellte Apple Intelligence inhaltlich vertiefen und vermutlich ein Signal der Einmütigkeit in der Apple-Chefetage ausstrahlen. Jetzt, nicht einmal zwei Jahre danach, wird Giannandrea das Unternehmen zur Monatsmitte verlassen.
Weiterlesen nach der Anzeige
Das Schicksal des früheren Google-Mitarbeiters ist eng verknüpft mit den KI-Bemühungen Apples. Damals angekündigte Grundfunktionen der Apple Intelligence wie das Zusammenfassen von Texten oder maßgeschneiderte Emojis erreichten zwar binnen weniger Monate einen großen Teil der Apple-Kundschaft. Der Blick der Fachwelt richtete sich allerdings mehr auf das, was nicht kam: Siri sei kein Sprachassistent mehr, sondern ein Geräteassistent mit tieferem Verständnis dafür, was auf dem Gerät passiert, tönte Giannandrea in der damaligen Fragestunde. Zwei Jahre später gibt es immer noch nur die Ankündigung, dass Siri besser werden soll. Zuletzt verdichteten sich jedoch Berichte, wonach Apple für iOS 27 eine eigene Siri-App sowie einen Chatbot-Modus plant.
KI-Weichen neu gestellt
Giannandrea kam im Jahr 2018 zu Apple. Bei Google war er zuvor acht Jahre lang tätig. Seine Expertise in Machine-Learning passte gut zu Apples Ambitionen, die mit der Neural Engine auch in Hardware gegossen wurden. Doch als mit ChatGPT von OpenAI Jahre später die generativen Sprachmodelle einen Hype auslösten, wirkte Apple wie auf dem falschen Fuß erwischt. Die Vorstellung der Apple Intelligence, 2024, sollte der Befreiungsschlag werden. Die „AI for the rest of us“ (KI für den Rest von uns) sollte mit dem Datenschutz-Vorrang punkten. Stattdessen machte Apple Schlagzeilen damit, dass die KI-Siri offenbar gar nicht funktionierte und das Projekt offenbar komplett zurückgesetzt werden musste. Intern gilt die Dauerbaustelle Siri bereits seit Längerem als Problemfall, da versprochene Funktionen immer wieder verschoben wurden.
Die diesjährige Entwicklerkonferenz WWDC am 8. Juni soll nun den Trubel der vergangenen Jahre vergessen machen. Apple ist eine Kooperation mit Google eingegangen, um deren KI-Modell Gemini zu verwenden. Vor allem aber wurden erhebliche Veränderungen in der Leitung vorgenommen. Dabei gilt Federighi intern als Verfechter eines eher pragmatischen KI-Kurses mit Fokus auf Budgets und Partnerschaften. Der ehemalige Vision-Pro-Chef brachte für den Umbau der Siri-Abteilung zahlreiche Experten aus seinem alten Team mit. Weitere erfahrene Kräfte, die es richten sollen, sind Eddy Cue und Sabih Khan. Erneut ist ein Ex-Googler mit in der Verantwortung: Neuzugang Amar Subramanya soll als Vice President of AI aber deutlich weniger Eigenständigkeit genießen als sein Vorgänger. Er berichte an Federighi.
Eine letzte Aktienprämie
Giannandrea war in den vergangenen Monaten nur noch Berater für Apple. Sein Rückzug wurde im Dezember 2025 angekündigt, nachdem zuvor bereits laut geworden war, dass es zwischen den Ressortleitern bei Apple geknirscht hat. Bereits im März 2025 habe Cook Giannandrea deshalb die Leitung des Siri-Teams entzogen, hieß es in Medienberichten. Es war ein Rückzug in Raten. Sein Verbleib bis Mitte April erklärt Bloomberg-Reporter Mark Gurman mit den üblichen Gepflogenheiten in börsennotierten Unternehmen, um Giannandrea noch eine letzte Aktienprämie auszahlen zu können.
Weiterlesen nach der Anzeige
Nach Apple will der frühere KI-Chef laut Bericht in verschiedenen Unternehmensvorständen mitwirken und als Berater für Start-up-Unternehmen tätig werden. Wie stark Apples stotternder KI-Motor tatsächlich mit seiner Person verknüpft war, wird sich in den nächsten Monaten zeigen. Finanziell profitiert der Konzern trotz technischer Rückstände massiv: Erwartungen zufolge wird Apple bald eine Milliarde US-Dollar durch KI-Apps im App Store verdienen.
Lesen Sie auch
(mki)
Entwicklung & Code
Gas Town 1.0.0: Agenten brauchen jetzt eine Freigabe
Gas Town hat Version 1.0.0 erreicht und gilt damit als produktionsreif. Nach 14 Vorabversionen (v0.5.0 bis v0.13.0) bringt das Release vor allem mehr Stabilität, bessere Sicherheit und neue Werkzeuge für die Orchestrierung komplexer Abläufe. Im Mittelpunkt stehen eine Workflow-basierte Ausführung, die direkte Anbindung an GitHub Merge Queues sowie zusätzliche Kontroll- und Schutzmechanismen für automatisierte Systeme.
Weiterlesen nach der Anzeige
Gas Town ist ein Open-Source-Projekt, das Entwicklungs- und Automatisierungsprozesse orchestriert – insbesondere mehrstufige, teils agentenbasierte Workflows. Konzepte wie „Formulas“, „Polecats“ oder „Refinery“ stehen für die Verbindung strukturierter Abläufe mit automatisierten Akteuren, die sich in bestehende Entwicklungsumgebungen wie GitHub einfügen. Entwickler arbeiten dazu nur mit einem einzigen, zentralen Agenten zusammen, dem Mayor, der die anderen Agenten selbsttätig ins Leben ruft und orchestriert.
Mehrstufige Workflows und GitHub-Merge-Queue-Anbindung
Die wichtigste funktionale Neuerung sind Workflow-Formeln. Der neue Typ „workflow“ in gt formula run erlaubt es, mehrstufige, interaktive Abläufe zu definieren und auszuführen. Statt einzelner Befehle lassen sich damit ganze Prozessketten abbilden – etwa Codegenerierung, Testlauf und Pull-Request-Erstellung in einem Durchgang. Für Entwicklungsteams entspricht das einer leichtgewichtigen Workflow-Engine, die besonders bei KI-gestützten Prozessen nützlich ist.
Dazu passend bindet die Komponente „Refinery“ jetzt nativ GitHub Merge Queues an. Mit merge_strategy=pr reiht Gas Town Pull Requests direkt in die Warteschlange ein. GitHub führt Änderungen dann seriell und erst nach erfolgreichem CI-Lauf zusammen. In automatisierten Setups entfällt damit ein guter Teil der eigenen Merge-Logik, gleichzeitig sinkt das Risiko von Konflikten durch parallele Änderungen.
Windows-Support
Version 1.0 bringt außerdem einen ersten Windows-Support. Gas Town implementiert dafür plattformspezifisches Signal-Handling, Prozessmanagement und die Nachverfolgung von tmux-Prozesshierarchien. Da sich diese Mechanismen unter Windows grundlegend von Unix unterscheiden, war eine eigene Umsetzung nötig. Teams mit gemischten Betriebssystemlandschaften können Gas Town damit erstmals durchgängig einsetzen.
Sicherheit und Governance
Weiterlesen nach der Anzeige
Beim Thema Sicherheit bündelt das Release mehrere Maßnahmen. Die Entwickler haben eine SQL-Injection-Lücke in dolt_remotes geschlossen. Ein neuer „PreToolUse“-Guard blockiert kritische Systemeingriffe wie sudo-Aufrufe oder Paketinstallationen. Zusätzlich lehnt Gas Town unsignierte Binärdateien ab. Die Kombination aus Schwachstellenbehebung und Laufzeitkontrollen zielt vor allem auf agentenbasierte Szenarien, in denen automatisierte Akteure potenziell gefährliche Aktionen auslösen könnten.
Neu sind auch „Mayor Approval Gates“ als Governance-Mechanismus. Bevor ein Polecat – also ein ausführendes Modul oder ein Agent – seinen Wirkungsbereich erweitert, braucht er eine Freigabe. Das betrifft etwa den Zugriff auf zusätzliche Ressourcen oder Komponenten. Das Prinzip ähnelt Policy-Engines oder Human-in-the-Loop-Ansätzen und soll verhindern, dass automatisierte Systeme eigenmächtig ihre Berechtigungen ausweiten.
Ein neues Rate-Limit-Watchdog-Plugin rundet das Release ab. Es erkennt HTTP-429-Antworten und stoppt den betroffenen Prozess automatisch. Das verhindert, dass Workflows bei API-Überlastung in Endlosschleifen laufen oder ungewollt hohe Kosten verursachen.
Alle Details zur Version 1.0.0 finden sich in den Release Notes auf der GitHub-Projektseite von Gas Town.
(fo)
Entwicklung & Code
Neu in .NET 10.0 [18]: Webserver als File-based App
Das direkte Übersetzen und Starten von C#-Dateien nennt Microsoft File-based Apps. Es ist auch möglich, einen Kestrel-Webserver innerhalb einer solchen App zu starten. Unten stehendes Beispiel zeigt eine ASP.NET Core Minimal WebAPI als eigenständige C#-Datei. Das Hosting erfolgt in dem in ASP.NET Core mitgelieferten Webserver Kestrel.
Weiterlesen nach der Anzeige

Dr. Holger Schwichtenberg ist technischer Leiter des Expertennetzwerks www.IT-Visions.de, das mit 53 renommierten Experten zahlreiche mittlere und große Unternehmen durch Beratungen und Schulungen sowie bei der Softwareentwicklung unterstützt. Durch seine Auftritte auf zahlreichen nationalen und internationalen Fachkonferenzen sowie mehr als 90 Fachbücher und mehr als 1500 Fachartikel gehört Holger Schwichtenberg zu den bekanntesten Experten für .NET und Webtechniken in Deutschland.
In der File-based App wird Microsoft.NET.Sdk.Web als SDK angegeben. Als NuGet-Pakete werden Microsoft.AspNetCore.OpenApi und Humanizer verwendet. Die Ahead-of-Time-Kompilierung, die in File-based Apps Standard ist, ist hier deaktiviert, um Warnungen des JSON-Serialisierers zu vermeiden.
Folgender Code zeigt den Webserver auf Basis von ASP.NET Core mit einem Minimal-WebAPI-Endpunkt und OpenAPI-Metadaten:
#:sdk Microsoft.NET.Sdk.Web
#:package Microsoft.AspNetCore.OpenApi@10.*-*
#:package Humanizer@2.14.1
#:property Version=1.1.4
#:property PublishAot=false
using Humanizer;
using Microsoft.OpenApi;
// Webserver einrichten
var builder = WebApplication.CreateBuilder();
builder.Services.AddOpenApi();
var app = builder.Build();
app.MapOpenApi(); //
app.MapGet("/", () =>
{
// Daten für Operation
var d = new Data
{
Version = "9.0",
Release = "2024-11-12"
};
var dotNet9Released = DateTimeOffset.Parse(d.Release);
var since = DateTimeOffset.Now - dotNet9Released;
return $"It has been {since.Humanize()} since .NET {d.Version} was released.";
});
app.MapGet("/data", () =>
{
var d = new Data
{
Version = "9.0",
Release = "2024-11-12"
};
return Results.Json(d);
});
app.Run();
class Data
{
///
/// Version of the .NET release.
///
public string? Version { get; set; }
///
/// Release date of the .NET version.
///
public string? Release { get; set; }
}

Start und Ausgabe des Webservers (Abb. 1)
(rme)
-
Künstliche Intelligenzvor 2 Monaten
Top 10: Die beste kabellose Überwachungskamera im Test – Akku, WLAN, LTE & Solar
-
Social Mediavor 1 MonatCommunity Management und Zielgruppen-Analyse: Die besten Insights aus Blog und Podcast
-
Social Mediavor 2 MonatenCommunity Management zwischen Reichweite und Verantwortung
-
UX/UI & Webdesignvor 2 MonatenEindrucksvolle neue Identity für White Ribbon › PAGE online
-
Entwicklung & Codevor 1 MonatCommunity-Protest erfolgreich: Galera bleibt Open Source in MariaDB
-
Künstliche Intelligenzvor 3 MonatenInterview: Massiver Anstieg der AU‑Fälle nicht durch die Telefon‑AU erklärbar
-
Entwicklung & Codevor 3 MonatenKommentar: Entwickler, wacht auf – oder verliert euren Job
-
Künstliche Intelligenzvor 2 MonatenSmartphone‑Teleaufsätze im Praxistest: Was die Technik kann – und was nicht
