Routing
Enterprise Integration Patterns · Gruppe 5 von 8
Routing ist das Herz der Integration: Wie gelangt eine Nachricht zu dem System, das sie verarbeiten soll? Diese Gruppe ist mit zwölf Mustern die größte. Die ersten vier entscheiden über den Weg einer Nachricht. Die nächsten drei zerlegen und verbinden Nachrichten oder bringen sie in Reihenfolge. Die letzten fünf sind zusammengesetzte Muster, die mehrere Bausteine zu größeren Abläufen kombinieren, bis hin zur zentralen Steuerung ganzer Geschäftsprozesse.
- Anwendung
- Integrationskomponente
- Kanal
- Nachricht
- Datenbank / Speicher
- 29. Content-Based Router
- 30. Message Filter
- 31. Dynamic Router
- 32. Recipient List
- 33. Splitter
- 34. Aggregator
- 35. Resequencer
- 36. Composed Message Processor
- 37. Scatter-Gather
- 38. Routing Slip
- 39. Process Manager
- 40. Message Broker
29. Content-Based Router · Inhaltsbasierter Router
Die Idee
Der Content-Based Router ist die häufigste Form des Message Routers: Er untersucht den Inhalt jeder Nachricht, also Header-Felder oder Werte im Body, und leitet sie danach an genau einen von mehreren Ausgangskanälen weiter. Die Regeln stehen an einer zentralen Stelle, nicht beim Sender und nicht bei den Empfängern.
Beispiel
Ein Händler hat drei Lager: eines für Bücher, eines für Elektronik und ein Kühllager für Lebensmittel. Der Shop schickt alle Bestellungen in einen Kanal. Der Router liest das Feld kategorie und verteilt die Bestellungen. Ob eine Bestellung nach Größe, Land, Kundenstatus oder Betrag verteilt wird, ist nur eine Frage der Regel.
Worauf achten
Der Router muss alle Ziele und ihre Zuständigkeiten kennen. Kommt ein neues Ziel hinzu, muss er angepasst werden. Das ist bei wenigen, stabilen Zielen gerade richtig, bei häufigen Änderungen hilft der Dynamic Router (Muster 31). Jeder Content-Based Router braucht außerdem eine Regel für Nachrichten, auf die keine Bedingung passt, etwa einen Standardkanal oder den Invalid Message Channel.
In der Praxis
In Apache Camel choice().when(header("kategorie").isEqualTo("buch")).to(...), in Spring Integration @Router oder HeaderValueRouter, in RabbitMQ Topic- und Header-Exchanges, die anhand von Routing-Keys bzw. Header-Werten verteilen.
30. Message Filter · Nachrichtenfilter
Die Idee
Ein Message Filter ist ein Router mit nur einem Ausgang: Nachrichten, die eine Bedingung erfüllen, lässt er durch. Alle anderen verwirft er. So erhält ein Empfänger nur die Nachrichten, die ihn wirklich interessieren, ohne sich selbst durch alle anderen arbeiten zu müssen.
Beispiel
Der Vertrieb möchte bei jeder Bestellung über 1.000 € benachrichtigt werden, um den Kunden persönlich anzurufen. Er hängt einen Filter an das Topic mit allen Bestellungen. Der Filter lässt nur große Bestellungen durch, der Vertrieb bekommt nur noch das, was er braucht.
Worauf achten
Filtern ist endgültig: Verworfene Nachrichten sind für diesen Weg weg. Das ist bei einem Publish-Subscribe Channel unproblematisch, weil jeder Abonnent seine eigene Kopie hat und filtern kann, wie er will. In einem Point-to-Point Channel dagegen kann ein Filter Nachrichten vernichten, die eigentlich jemand anderes gebraucht hätte. Für „mehrere Empfänger mit verschiedenen Interessen“ gibt es zwei Lösungen: ein Topic mit einem Filter pro Abonnent, oder einen Content-Based Router.
In der Praxis
filter() in Camel, @Filter in Spring Integration, Message Selectors in JMS (betrag > 1000 als SQL-ähnlicher Ausdruck), Filter-Policies bei Amazon SNS. In Java Streams ist filter() dasselbe Prinzip für Daten im Speicher.
31. Dynamic Router · Dynamischer Router
Die Idee
Beim Content-Based Router sind die Regeln fest einprogrammiert. Der Dynamic Router hält seine Routing-Tabelle zur Laufzeit aktuell. Die möglichen Empfänger melden sich über einen eigenen Steuerkanal bei ihm an und teilen mit, für welche Nachrichten sie zuständig sind. Der Router trägt das in seine Tabelle ein und verteilt ab sofort danach.
Beispiel
Ein Händler eröffnet ein neues Lager in Zürich. Beim Start schickt das Lager eine Nachricht an den Steuerkanal: „Ich bin zuständig für Lieferungen in die Schweiz.“ Der Router übernimmt die Regel. Niemand muss den Router umprogrammieren oder neu starten. Schließt das Lager, meldet es sich wieder ab.
Worauf achten
Die Flexibilität hat ihren Preis: Was passiert, wenn sich zwei Empfänger für dasselbe melden? Wenn ein Empfänger abstürzt, ohne sich abzumelden? Wenn der Router neu startet und seine Tabelle vergisst? Ein Dynamic Router braucht deshalb Regeln für Konflikte, eine dauerhafte Speicherung seiner Tabelle und am besten regelmäßige Lebenszeichen der Empfänger. Außerdem lässt sich das Verhalten des Systems schwerer nachvollziehen, weil die Regeln nirgends im Code stehen.
In der Praxis
Service-Registries wie Consul oder Eureka zusammen mit Load-Balancern, die bei der Registry nachschlagen, sind die heute verbreitetste Form. In Camel gibt es dynamicRouter() mit einer Methode, die bei jeder Nachricht das Ziel berechnet.
32. Recipient List · Empfängerliste
Die Idee
Eine Recipient List schickt eine Nachricht an mehrere Empfänger, aber nicht pauschal an alle wie ein Publish-Subscribe Channel. Für jede Nachricht wird einzeln berechnet, wer sie bekommen soll. Dann erhält jeder Empfänger auf der Liste eine Kopie.
Beispiel
Eine Inlandsbestellung geht an das Lager und die Buchhaltung. Eine Bestellung ins Nicht-EU-Ausland geht zusätzlich an das System für Zollmeldungen. Eine Bestellung mit Gefahrgut geht außerdem an die Sicherheitsabteilung. Die Recipient List prüft jede Bestellung und stellt die passende Liste zusammen.
Worauf achten
Die Liste kann auf zwei Arten entstehen: Die Recipient List berechnet sie anhand von Regeln, oder der Sender schreibt sie gleich in den Header der Nachricht. Im zweiten Fall muss der Sender seine Empfänger kennen, was die Entkopplung schwächt. Der Unterschied zu einem Publish-Subscribe Channel: Dort entscheiden die Empfänger, ob sie zuhören. Hier entscheidet die Recipient List, wer etwas bekommt. Werden die Antworten der Empfänger anschließend wieder eingesammelt, entsteht Scatter-Gather (Muster 37).
In der Praxis
recipientList() in Camel, RecipientListRouter in Spring Integration, und im Alltag jede E-Mail mit mehreren Empfängern in An und CC.
33. Splitter · Aufteiler
Die Idee
Ein Splitter nimmt eine Nachricht, die mehrere Elemente enthält, und zerlegt sie in einzelne Nachrichten, eine pro Element. Jedes Element kann danach unabhängig weiterverarbeitet, geroutet oder parallel bearbeitet werden.
Beispiel
Eine Bestellung enthält ein Buch, Kopfhörer und Käse. Diese drei Artikel kommen aus drei verschiedenen Lagern. Der Splitter erzeugt aus der Bestellung drei Positionsnachrichten. Ein nachgeschalteter Content-Based Router schickt jede an das richtige Lager.
Worauf achten
Jede Teilnachricht sollte genug Kontext mitnehmen, um allein verständlich zu sein: Bestellnummer, Kunde, Lieferadresse. Damit die Teile später wieder zusammengeführt werden können, bekommen sie eine gemeinsame Kennung, ihre Nummer und die Gesamtzahl der Teile (wie bei der Message Sequence aus Gruppe 4). Der Splitter hat sein natürliches Gegenstück im Aggregator (Muster 34).
In der Praxis
split() in Camel, etwa split(body().tokenize("\n")) für eine Zeile pro Nachricht, @Splitter in Spring Integration. In Java Streams entspricht flatMap() dem Splitter.
34. Aggregator · Zusammenführer
Die Idee
Der Aggregator ist das Gegenstück zum Splitter: Er sammelt zusammengehörige Nachrichten und erzeugt daraus eine einzige neue Nachricht. Dafür muss er zustandsbehaftet sein. Er merkt sich die bisher eingegangenen Teile, bis die Gruppe vollständig ist.
Beispiel
Die drei Positionen einer Bestellung wurden von drei Lagern verschickt, und jedes Lager meldet seinen Versand. Der Kunde soll aber nicht drei E-Mails bekommen. Der Aggregator sammelt die Versandmeldungen nach Bestellnummer, und erst wenn alle drei vorliegen, erzeugt er eine Nachricht „Ihre Bestellung ist vollständig versandt“.
Worauf achten
Jeder Aggregator braucht Antworten auf drei Fragen:
- Korrelation: Welche Nachrichten gehören zusammen? Meist über eine gemeinsame ID wie die Bestellnummer.
- Vollständigkeit: Wann ist eine Gruppe fertig? Wenn alle erwarteten Teile da sind, nach einer festen Wartezeit, oder sobald das erste gute Ergebnis vorliegt.
- Zusammenführung: Wie wird aus vielen Nachrichten eine? Alle sammeln, summieren, das beste Ergebnis wählen.
Besonders wichtig ist der Fall, dass ein Teil nie ankommt. Ohne Zeitlimit wartet der Aggregator ewig, und unvollständige Gruppen füllen nach und nach den Speicher.
In der Praxis
aggregate() in Camel mit AggregationStrategy und completionSize() bzw. completionTimeout(), @Aggregator mit ReleaseStrategy in Spring Integration, Windowing in Kafka Streams. In Java Streams entspricht Collectors.groupingBy() dem Grundgedanken.
35. Resequencer · Neuordner
Die Idee
Nachrichten können unterwegs ihre Reihenfolge verlieren, etwa weil sie über verschiedene Wege laufen oder parallel verarbeitet werden. Der Resequencer puffert eingehende Nachrichten und gibt sie erst in der richtigen Reihenfolge weiter. Dafür braucht jede Nachricht eine laufende Nummer.
Beispiel
Ein Lager meldet Buchungen: 1 Wareneingang, 2 Entnahme, 3 Inventurkorrektur. Weil die Meldungen von mehreren Workern parallel verschickt werden, kommen sie in der Reihenfolge 3, 1, 2 an. Die Buchhaltung braucht sie aber in der richtigen Reihenfolge, sonst wäre der Bestand zwischendurch negativ. Der Resequencer hält Nachricht 3 fest, gibt 1 sofort weiter, dann 2 und schließlich 3.
Worauf achten
Fehlt eine Nachricht dauerhaft, staut sich alles hinter ihr. Der Resequencer braucht deshalb eine Höchstwartezeit und eine Regel, was dann passiert: die Lücke melden und weitermachen, oder anhalten. Außerdem muss die Pufferkapazität begrenzt sein. Oft lässt sich das Problem vermeiden, indem man von vornherein dafür sorgt, dass zusammengehörige Nachrichten denselben Weg nehmen, wie es Kafka mit Partitionen macht.
In der Praxis
resequence() in Camel mit Batch- oder Stream-Modus, Resequencer in Spring Integration. Im Netzwerk erledigt TCP genau diese Aufgabe für Datenpakete.
36. Composed Message Processor · Zusammengesetzter Verarbeiter
Die Idee
Der Composed Message Processor ist kein neuer Baustein, sondern eine bewährte Kombination aus drei bekannten: Ein Splitter zerlegt eine Nachricht in Teile, ein Router schickt jeden Teil an das zuständige System, und ein Aggregator setzt die Ergebnisse wieder zu einer Nachricht zusammen. Von außen sieht das Ganze aus wie ein einziger Filter: eine Nachricht rein, eine raus.
Beispiel
Vor der Bestätigung einer Bestellung soll jede Position auf Verfügbarkeit geprüft werden. Bücher prüft das Bücherlager, Elektronik das Technik-Lager, Lebensmittel das Kühllager. Der Splitter zerlegt die Bestellung, der Router schickt jede Position an ihr Lager, und der Aggregator führt die Antworten zu „Position 1 lieferbar, Position 2 lieferbar, Position 3 in zwei Tagen“ zusammen.
Worauf achten
Alle Schwierigkeiten der Einzelteile treffen hier zusammen: Der Aggregator muss wissen, wie viele Teile er erwarten kann, und er braucht ein Zeitlimit für Lager, die nicht antworten. Der Unterschied zu Scatter-Gather (Muster 37): Hier bekommt jedes Ziel einen anderen Teil der Nachricht. Bei Scatter-Gather bekommen alle Ziele dieselbe Anfrage.
In der Praxis
In Camel lässt sich das kompakt mit split(..., aggregationStrategy) bauen, wobei der Splitter die Ergebnisse selbst einsammelt. Typische Einsatzgebiete sind Verfügbarkeitsprüfungen, Preisberechnungen pro Position und die Validierung zusammengesetzter Dokumente.
37. Scatter-Gather · Verteilen und Einsammeln
Die Idee
Scatter-Gather schickt dieselbe Anfrage an mehrere Empfänger (Scatter, „verstreuen“) und sammelt deren Antworten anschließend wieder ein (Gather, „einsammeln“). Ein Aggregator fasst die Antworten zu einem Ergebnis zusammen, etwa indem er die beste auswählt oder alle zusammenführt.
Beispiel
Der Einkauf braucht 500 Kartons. Er schickt eine Preisanfrage gleichzeitig an drei Lieferanten. Die Angebote kommen nach und nach zurück: 12,40 €, 11,90 € und 13,10 €. Der Aggregator wartet höchstens zwei Minuten und gibt dann das günstigste Angebot, Lieferant B, an den Einkauf weiter.
Worauf achten
Verteilt wird entweder über einen Publish-Subscribe Channel, bei dem jeder interessierte Lieferant zuhören kann, oder über eine Recipient List mit einer festen Auswahl. Entscheidend ist die Abschlussregel des Aggregators: auf alle warten, bis zu einem Zeitlimit warten, oder schon beim ersten ausreichend guten Angebot aufhören. Ein langsamer oder ausgefallener Empfänger darf nicht das ganze Ergebnis blockieren.
In der Praxis
Preisvergleiche und Ausschreibungen, Flug- und Hotelsuchen, die viele Anbieter parallel abfragen, verteilte Suchen über mehrere Datenbanken. In Camel multicast().parallelProcessing() mit einer AggregationStrategy, in Spring Integration ScatterGatherHandler.
38. Routing Slip · Laufzettel
Die Idee
Bei einem Routing Slip wird die Route einer Nachricht gleich am Anfang festgelegt und als Liste von Stationen in den Header geschrieben, wie ein Laufzettel, der an einer Akte klebt. Jede Station erledigt ihre Arbeit, hakt sich auf dem Zettel ab und schickt die Nachricht an die nächste Station auf der Liste. Die Stationen selbst kennen einander nicht.
Beispiel
Bestellungen durchlaufen je nach Art unterschiedliche Schritte. Jede wird auf eine gültige Adresse geprüft und bekommt die Steuer berechnet. Wurde Geschenkverpackung gewählt, kommt ein dritter Schritt hinzu, bei Firmenkunden eine Bonitätsprüfung. Am Eingang wird für jede Bestellung der passende Laufzettel erstellt. Danach läuft sie ohne zentrale Steuerung durch genau die Stationen, die sie braucht.
Worauf achten
Der Routing Slip eignet sich für lineare Abläufe, deren Schritte am Anfang feststehen. Verzweigungen, Schleifen oder Entscheidungen, die erst von einem Zwischenergebnis abhängen, lassen sich damit kaum abbilden. Dafür gibt es den Process Manager (Muster 39). Ein Vorteil: Weil die Route in der Nachricht steht, lässt sich jederzeit nachvollziehen, wo eine Nachricht gerade ist und was noch fehlt.
In der Praxis
routingSlip() in Camel mit einem Header, der die Liste der Endpunkte enthält, RoutingSlip in Spring Integration. In verteilten Systemen gibt es denselben Gedanken bei choreografierten Sagas, in denen jeder Dienst den nächsten Schritt aus der Nachricht liest.
39. Process Manager · Prozesssteuerung
Die Idee
Für komplexe Abläufe mit Verzweigungen, Wartezeiten und Fehlerbehandlung gibt es eine zentrale Steuerung: den Process Manager. Er kennt den gesamten Ablauf, schickt jedem beteiligten Dienst zur richtigen Zeit einen Auftrag, wartet auf dessen Antwort und entscheidet dann über den nächsten Schritt. Für jeden laufenden Vorgang speichert er den aktuellen Stand.
Beispiel
Eine Bestellung durchläuft: Zahlung einziehen, Ware reservieren, Versand beauftragen. Schlägt die Zahlung fehl, soll der Kunde benachrichtigt und die Reservierung aufgehoben werden. Ist ein Artikel nicht vorrätig, wird zwei Tage gewartet und erneut geprüft. Der Process Manager bildet diese Regeln ab. Die Dienste Zahlung, Lager und Versand kennen den Ablauf nicht, sie erledigen nur ihre jeweilige Aufgabe.
Worauf achten
Der Process Manager ist das mächtigste Routing-Muster. Er kann alles, was die anderen können, und mehr. Dafür ist er auch das aufwendigste: Er braucht einen dauerhaften Speicher für seine Vorgänge, muss Zeitüberschreitungen erkennen und im Fehlerfall bereits erledigte Schritte rückgängig machen können. Der Gegenentwurf ist die Choreografie, bei der Dienste nur auf Events reagieren und es keine zentrale Steuerung gibt. Das ist lockerer gekoppelt, aber der Gesamtablauf ist nirgends sichtbar.
In der Praxis
Workflow-Engines wie Camunda, Temporal oder AWS Step Functions, orchestrierte Sagas in Microservice-Architekturen, BPMN-Prozesse. In Camel und Spring Integration lassen sich einfache Process Manager mit Routen und einem Zustandsspeicher bauen.
40. Message Broker · Nachrichtenvermittler
Die Idee
Ein Message Broker ist ein zentraler Vermittler, über den alle Anwendungen ihre Nachrichten austauschen. Statt dass jede Anwendung jede andere kennt und mit ihr verbunden ist, kennt jede nur den Broker. Er übernimmt Routing, Übersetzung und Zustellung. Das Buch beschreibt ihn als Muster auf Architekturebene: Viele der anderen Routing-Muster werden im Broker zusammengefasst.
Beispiel
Ein Unternehmen hat sechs Anwendungen, die Daten austauschen. Direkt miteinander verbunden wären das bis zu 15 einzelne Schnittstellen, und bei der siebten Anwendung kämen sechs weitere hinzu. Mit einem Broker hat jede Anwendung genau einen Anschluss. Eine neue Anwendung braucht ebenfalls nur einen.
Worauf achten
Diese Architektur heißt auch Hub and Spoke, Nabe und Speichen. Ihr Vorteil ist die Übersicht: Alle Integrationsregeln liegen an einem Ort. Ihr Risiko ist dasselbe: Der Broker wird zum Engpass und zum zentralen Ausfallpunkt. Er muss deshalb hochverfügbar und skalierbar betrieben werden. Wandert zu viel Geschäftslogik in den Broker, entsteht ein schwer wartbarer Zentralbaustein. Der heutige Trend geht deshalb zu „dummen“ Brokern, die nur transportieren, während die Logik in den Anwendungen bleibt.
In der Praxis
Das Wort „Broker“ wird heute meist für die Messaging-Server selbst verwendet: RabbitMQ, ActiveMQ, Kafka. Im Sinne des Musters, also mit zentraler Routing- und Übersetzungslogik, entsprechen ihm eher Integrationsplattformen wie Apache Camel als zentraler Dienst, MuleSoft oder klassische Enterprise Service Buses.
Die Routing-Muster im Überblick
| Muster | Eingang → Ausgang | Entscheidet anhand |
|---|---|---|
| Content-Based Router | 1 → einer von vielen | Inhalt der Nachricht, feste Regeln |
| Message Filter | 1 → 1 oder keiner | Bedingung |
| Dynamic Router | 1 → einer von vielen | Regeln, die sich zur Laufzeit ändern |
| Recipient List | 1 → mehrere | berechnete Empfängerliste |
| Splitter | 1 Nachricht → viele Nachrichten | Struktur des Inhalts |
| Aggregator | viele Nachrichten → 1 Nachricht | Korrelation und Vollständigkeit |
| Resequencer | viele → viele, sortiert | laufende Nummer |
| Composed Message Processor | 1 → 1 (intern zerlegt) | Splitter + Router + Aggregator |
| Scatter-Gather | 1 → 1 (intern verteilt) | Verteilen + Aggregator |
| Routing Slip | feste Folge von Stationen | Laufzettel in der Nachricht |
| Process Manager | beliebiger Ablauf | zentraler Zustand und Ablaufregeln |
| Message Broker | alle ↔ alle über einen Knoten | Architektur: Nabe und Speichen |