Nachrichtenaufbau
Enterprise Integration Patterns · Gruppe 4 von 8
Diese Gruppe beschäftigt sich mit der Nachricht selbst: Wozu dient sie, und welche Angaben braucht sie im Header, damit das Zusammenspiel funktioniert? Die ersten drei Muster beschreiben den Zweck einer Nachricht: Auftrag, Daten oder Ereignis. Die nächsten drei regeln, wie aus Einbahnstraßen Anfrage und Antwort werden. Die letzten drei sind Header-Angaben für Sonderfälle: große Daten, verfallende Daten und sich ändernde Formate.
- Anwendung
- Integrationskomponente
- Kanal
- Nachricht
- Datenbank / Speicher
- 20. Command Message
- 21. Document Message
- 22. Event Message
- 23. Request-Reply
- 24. Return Address
- 25. Correlation Identifier
- 26. Message Sequence
- 27. Message Expiration
- 28. Format Indicator
20. Command Message · Befehlsnachricht
Die Idee
Eine Command Message ist ein Auftrag: Sie fordert den Empfänger auf, etwas Bestimmtes zu tun. Sie ist das Messaging-Gegenstück zu einem Methodenaufruf, nur dass der Aufrufer nicht wartet. Ihr Name ist meist ein Verb im Imperativ: VersendeBestellung, ErstelleRechnung, SperreKonto. Das entspricht dem Command-Muster der GoF: Ein Aufruf wird zu einem Objekt, das man verschicken und speichern kann.
Beispiel
Nachdem die Zahlung eingegangen ist, schickt der Shop die Nachricht VersendeBestellung(4711) an das Lager. Das Lager führt den Auftrag aus, sobald es dazu kommt. Ob es dafür eine Minute oder eine Stunde braucht, muss den Shop nicht kümmern.
Worauf achten
Ein Befehl hat einen bestimmten Empfänger, der zuständig ist, und soll genau einmal ausgeführt werden. Er gehört deshalb in einen Point-to-Point Channel. Weil Nachrichten im Fehlerfall doppelt ankommen können, sollte der Befehl so gestaltet sein, dass eine Wiederholung keinen Schaden anrichtet: Eine Bestellung, die schon versendet wurde, wird nicht ein zweites Mal verschickt.
In der Praxis
Aufträge an Hintergrunddienste wie „Erzeuge PDF“ oder „Sende E-Mail“, Job-Queues, die Command-Seite in CQRS-Architekturen und die Schritte einer Saga in Microservice-Abläufen.
21. Document Message · Dokumentnachricht
Die Idee
Eine Document Message transportiert Daten, keine Anweisung. Der Sender liefert einen vollständigen Datensatz, etwa einen Kunden, eine Rechnung oder einen Katalog, und der Empfänger entscheidet selbst, was er damit anfängt. Der Inhalt ist das Wichtige, nicht der Zeitpunkt.
Beispiel
Das CRM-System verschickt die kompletten Stammdaten eines Kunden, nachdem sie geändert wurden. Die Buchhaltung speichert die neue Rechnungsadresse, das Versandsystem die neue Lieferadresse, und das Reporting zählt nur mit. Keines der Systeme muss beim CRM nachfragen, alle nötigen Daten stehen in der Nachricht.
Worauf achten
Document Messages sind oft größer als die anderen Arten, weil sie den vollständigen Zustand enthalten. Das macht den Empfänger unabhängig, bläht aber die Nachrichten auf. Bei sehr großen Dokumenten verschickt man besser nur einen Verweis (Claim Check, Gruppe 6). Die Grenze zu den anderen Arten ist fließend: Eine Antwort auf eine Anfrage ist meist eine Document Message, und viele Event Messages enthalten zusätzlich die geänderten Daten.
In der Praxis
Stammdatenabgleich zwischen Systemen, EDI-Nachrichten im Handel (Rechnungen, Lieferscheine), Antworten auf Abfragen und sogenannte Event-Carried State Transfer-Ereignisse, die den neuen Zustand gleich mitliefern.
22. Event Message · Ereignisnachricht
Die Idee
Eine Event Message meldet ein Ereignis: Etwas ist passiert. Der Sender gibt keine Anweisung und erwartet keine Antwort. Er teilt nur mit, was geschehen ist, und überlässt es den Empfängern, ob und wie sie reagieren. Ihr Name steht deshalb in der Vergangenheit: BestellungEingegangen, ZahlungErhalten, KundeUmgezogen.
Beispiel
Der Shop meldet BestellungEingegangen. Das Lager reserviert die Ware, die Buchhaltung bereitet eine Rechnung vor, und der Newsletter-Dienst merkt sich das Kaufinteresse. Der Shop weiß nicht, wer zuhört. Kommt ein neuer Dienst hinzu, etwa ein Betrugserkennungssystem, abonniert er das Ereignis, und der Shop bleibt unverändert.
Worauf achten
Der Unterschied zwischen Command und Event ist grundlegend: Ein Command sagt „Tu das!“ und hat genau einen Empfänger, der zuständig ist. Ein Event sagt „Das ist passiert“ und hat beliebig viele oder auch gar keine Empfänger. Events gehören deshalb in einen Publish-Subscribe Channel. Bei Events gibt es zwei Stile: dünne Events mit nur einer ID (Empfänger müssen Details nachfragen) und dicke Events, die alle relevanten Daten mitliefern.
In der Praxis
Die Grundlage ereignisgetriebener Architekturen, Domain Events aus dem Domain-Driven Design, Event Sourcing, Kafka-Topics mit Geschäftsereignissen und Webhooks, die externe Dienste über Ereignisse informieren.
23. Request-Reply · Anfrage und Antwort
Die Idee
Messaging ist von Natur aus eine Einbahnstraße: Eine Nachricht geht vom Sender zum Empfänger, und damit ist der Vorgang beendet. Braucht der Sender eine Antwort, verwendet man zwei Kanäle: einen für die Anfrage und einen für die Antwort. Der Empfänger der Anfrage wird dabei zum Absender der Antwort.
Beispiel
Der Shop schickt eine Anfrage bestand("4711") in den Kanal bestand-anfragen. Das Lager prüft den Bestand und schickt „12 Stück“ in den Kanal bestand-antworten. Der Shop liest die Antwort von dort.
Worauf achten
Der Anfragende kann auf zwei Arten auf die Antwort warten: blockierend, indem er nach dem Senden im Antwortkanal wartet (einfach, aber dann ähnelt das Ganze stark einem direkten Aufruf), oder asynchron, indem er weiterarbeitet und die Antwort verarbeitet, sobald sie kommt. Zwei Fragen stellen sich immer: Woher weiß der Empfänger, wohin er antworten soll (Return Address, Muster 24)? Und welche Antwort gehört zu welcher Anfrage (Correlation Identifier, Muster 25)? Außerdem braucht jede Anfrage ein Zeitlimit für den Fall, dass nie eine Antwort kommt.
In der Praxis
Das Header-Feld JMSReplyTo mit temporären Antwort-Queues in JMS, das reply_to-Feld in RabbitMQ (das Tutorial nennt es „RPC“), ReplyingKafkaTemplate in Spring Kafka und inOut-Routen in Apache Camel.
24. Return Address · Rücksendeadresse
Die Idee
Ein Dienst, der Anfragen beantwortet, kann viele verschiedene Auftraggeber haben. Damit er nicht fest einprogrammieren muss, wohin jede Antwort geht, schreibt der Anfragende den Antwortkanal in den Header der Anfrage, wie einen Absender auf einem Briefumschlag. Der Antwortende liest das Feld und schickt die Antwort genau dorthin.
Beispiel
Sowohl der Online-Shop als auch die Kassen in den Filialen fragen beim Lager den Bestand ab, beide über denselben Kanal bestand-anfragen. Der Shop setzt im Header antwort-an: shop-antworten, die Kasse antwort-an: kasse-antworten. Das Lager muss die beiden nicht kennen. Kommt eine App hinzu, gibt sie einfach ihren eigenen Antwortkanal an.
Worauf achten
Die Return Address macht den antwortenden Dienst wiederverwendbar, weil er nichts über seine Auftraggeber wissen muss. Häufig wird für jede einzelne Anfrage oder jede Instanz des Anfragenden ein eigener, temporärer Antwortkanal angelegt, der nach Erhalt der Antwort wieder verschwindet. Fehlt die Return Address, sollte der Dienst das als Fehler behandeln, statt die Antwort in einen Standardkanal zu werfen.
In der Praxis
JMSReplyTo in JMS, die Eigenschaft reply_to in RabbitMQ (oft mit der eingebauten Direct-Reply-To-Pseudo-Queue), der Header KafkaHeaders.REPLY_TOPIC in Spring Kafka. Bei E-Mail erfüllt der Header Reply-To genau denselben Zweck.
25. Correlation Identifier · Korrelationskennung
Die Idee
Stellt ein System viele Anfragen hintereinander, kommen die Antworten nicht unbedingt in derselben Reihenfolge zurück. Damit der Anfragende jede Antwort richtig zuordnen kann, bekommt jede Anfrage eine eindeutige Kennung. Der Antwortende kopiert sie in seine Antwort, in ein eigenes Header-Feld, die Correlation ID. Wie ein Aktenzeichen, das man bei jedem Schreiben an eine Behörde angibt.
Beispiel
Der Shop fragt den Bestand für drei Artikel ab. Die Anfragen haben die IDs 1, 2 und 3. Das Lager beantwortet die einfache Anfrage 1 sofort, für Anfrage 3 schaut es in einem zweiten Lager nach, und Anfrage 2 dauert am längsten. Die Antworten kommen in der Reihenfolge 1, 3, 2. Über das Feld „zu“ weiß der Shop dennoch genau, welcher Bestand zu welchem Artikel gehört.
Worauf achten
Die Kennung muss eindeutig sein, typischerweise eine UUID. Meist verwendet man einfach die Nachrichten-ID der Anfrage als Correlation ID der Antwort. Der Anfragende muss sich offene Anfragen merken, etwa in einer Map von ID auf wartende Aufgabe, und Einträge entfernen, deren Antwort zu lange ausbleibt. Correlation IDs sind auch über Request-Reply hinaus nützlich: Wandert eine ID durch alle Nachrichten eines Geschäftsvorgangs, lässt sich der Vorgang später in den Logs aller Systeme verfolgen.
In der Praxis
JMSCorrelationID in JMS, correlation_id in RabbitMQ, KafkaHeaders.CORRELATION_ID in Spring Kafka. Tracing-Systeme wie OpenTelemetry tragen nach demselben Prinzip eine Trace-ID durch alle beteiligten Dienste.
26. Message Sequence · Nachrichtenfolge
Die Idee
Manche Daten sind zu groß für eine einzelne Nachricht, weil Broker oft eine Größengrenze haben. Dann zerlegt der Sender sie in mehrere Teilnachrichten. Jede trägt drei Angaben im Header: eine gemeinsame Sequenz-ID, die alle Teile verbindet, ihre Position in der Folge und eine Kennung, welcher Teil der letzte ist. Der Empfänger sammelt die Teile und setzt sie wieder zusammen.
Beispiel
Der Großhändler schickt jeden Monat seinen vollständigen Produktkatalog von 30 MB an den Shop. Der Broker erlaubt höchstens 10 MB pro Nachricht. Der Katalog wird deshalb in drei Nachrichten mit der Sequenz-ID K-2026-10 und den Positionen 1, 2 und 3 verschickt, wobei Teil 3 als letzter markiert ist. Der Shop wartet, bis alle drei da sind, und importiert dann den Katalog.
Worauf achten
Die Teile können in falscher Reihenfolge ankommen, einzelne können fehlen oder doppelt eintreffen. Der Empfänger muss also puffern, sortieren und nach einer Wartezeit entscheiden, was mit unvollständigen Folgen passiert. Das Sortieren übernimmt der Resequencer, das Zusammenfügen der Aggregator aus Gruppe 5. Oft ist es einfacher, die Daten abzulegen und nur einen Verweis zu verschicken (Claim Check, Gruppe 6).
In der Praxis
Die JMS-Gruppierung mit den Eigenschaften JMSXGroupID und JMSXGroupSeq, das Paar Splitter und Aggregator in Camel und Spring Integration mit den Headern sequenceNumber und sequenceSize, Multipart-Uploads bei Cloud-Speichern.
27. Message Expiration · Ablaufzeit
Die Idee
Manche Nachrichten sind nur für eine begrenzte Zeit sinnvoll. Der Sender gibt ihnen deshalb eine Ablaufzeit mit, entweder als festen Zeitpunkt oder als Lebensdauer. Ist sie überschritten, bevor die Nachricht zugestellt wurde, wird sie nicht mehr ausgeliefert. Das Messaging-System verwirft sie oder legt sie in den Dead Letter Channel.
Beispiel
Eine Börse verschickt Aktienkurse mit einer Gültigkeit von einer Minute. Die Kurs-App war kurz offline und meldet sich um 12:07 Uhr zurück. Der Kurs von 12:04 Uhr ist inzwischen abgelaufen und wird nicht mehr zugestellt. Die App zeigt nur aktuelle Kurse an, statt erst einen Berg veralteter Daten abzuarbeiten.
Worauf achten
Die Ablaufzeit schützt vor zwei Problemen: davor, dass Empfänger veraltete Informationen verarbeiten, und davor, dass sich bei einem Ausfall ein riesiger Rückstau bildet. Zeitangaben setzen voraus, dass die Uhren aller beteiligten Server ausreichend gleich gehen. Die Ablaufzeit greift nur bis zur Zustellung. Hat der Empfänger die Nachricht schon erhalten, muss er selbst prüfen, ob sie noch gültig ist.
In der Praxis
setTimeToLive() bzw. das Feld JMSExpiration in JMS, die Eigenschaft expiration pro Nachricht und x-message-ttl pro Queue in RabbitMQ. Typische Einsatzgebiete sind Kurse, Positionsdaten, Einmalpasswörter und zeitkritische Benachrichtigungen.
28. Format Indicator · Formatkennzeichen
Die Idee
Nachrichtenformate ändern sich mit der Zeit: Felder kommen hinzu, werden umbenannt oder anders aufgebaut. Damit ein Empfänger weiß, wie er eine Nachricht lesen muss, trägt sie ein Formatkennzeichen, meist eine Versionsnummer im Header. Der Empfänger liest zuerst das Kennzeichen und wählt dann den passenden Weg, den Inhalt zu verarbeiten.
Beispiel
Der Shop wird schrittweise auf eine neue Version umgestellt. In Version 2 hat die Bestellung zusätzlich eine Mengeneinheit, damit auch Meterware verkauft werden kann. Eine Zeit lang laufen alte und neue Shop-Server parallel. Das Lager liest das Feld format und verarbeitet v1-Nachrichten wie bisher, v2-Nachrichten mit Einheit.
Worauf achten
Das Buch nennt drei Varianten: eine Versionsnummer, einen Verweis auf eine Formatbeschreibung (etwa eine Schema-ID) oder eine Formatangabe im Dokument selbst (wie die Namespace-Angabe in XML). Ohne Kennzeichen bleibt dem Empfänger nur, das Format zu erraten. Das funktioniert, bis zwei Versionen sich zu ähnlich sind. Gut ist es außerdem, Formate möglichst abwärtskompatibel zu ändern, etwa nur optionale Felder hinzuzufügen, damit alte Empfänger neue Nachrichten trotzdem verstehen.
In der Praxis
Schema-IDs in der Confluent Schema Registry für Avro und Protobuf, der Content-Type-Header mit Versionsangabe wie application/vnd.firma.bestellung.v2+json, die Eigenschaft type in CloudEvents und Namespaces in XML.
Command, Document und Event im Vergleich
| Command Message | Document Message | Event Message | |
|---|---|---|---|
| Aussage | „Tu das!“ | „Hier sind Daten.“ | „Das ist passiert.“ |
| Name | Verb im Imperativ: VersendeBestellung | Substantiv: Kunde, Rechnung | Vergangenheit: BestellungEingegangen |
| Empfänger | genau einer, der zuständig ist | einer oder mehrere | beliebig viele, auch keiner |
| Typischer Kanal | Point-to-Point | Point-to-Point oder Publish-Subscribe | Publish-Subscribe |
| Wichtig ist | die Ausführung | der Inhalt | der Zeitpunkt |
| Der Sender ... | erwartet, dass etwas passiert | liefert, was gebraucht wird | weiß nicht, wer reagiert |