Point-to-Point Queue
Eine Nachricht wird von genau einem Consumer verarbeitet.
Entkopplung ueber Queues, garantierte Zustellung, Dead Letter Queues, Retry, Reihenfolge und idempotente Verarbeitung.
Asynchrone Integration ist ein typisches Herzstueck gewachsener Enterprise-Landschaften. MQ entkoppelt Systeme, schuetzt Backends vor Lastspitzen und erlaubt Wiederanlauf. Gleichzeitig macht es Ursache-Wirkung schwerer sichtbar.
JMS-Code sieht oft einfach aus: Nachricht empfangen, verarbeiten, bestaetigen. Die echten Fragen sind aber: Was passiert bei Timeout? Ist die Verarbeitung idempotent? Wie erkennt der Fachbereich haengende Nachrichten? Was passiert mit Poison Messages?
Modernisierung muss Queue-Semantik erhalten. Ein Wechsel auf Kafka, RabbitMQ oder Cloud Messaging ist kein reines Tool-Thema, sondern betrifft Ordering, Transaktionsgrenzen, Retention, Reprocessing und fachlichen Ausgleich.
Eine Nachricht wird von genau einem Consumer verarbeitet.
Ablage nach mehrfach fehlgeschlagener Verarbeitung.
Dead Letter Queue fuer unzustellbare Nachrichten.
Fachlicher Schluessel, der doppelte Verarbeitung verhindert.
Die Beispiele sind bewusst nicht minimalistisch. Sie zeigen typische Artefakte, die man in echten Legacy-Analysen findet: Schnittstellenverträge, Containerkonfiguration, SQL/PL-SQL, Jobdefinitionen, Queue-Regeln oder Adaptercode.
@MessageDriven(activationConfig = {
@ActivationConfigProperty(propertyName = "destinationLookup", propertyValue = "jms/InvoiceEvents"),
@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue")
})
public class InvoiceEventConsumer implements MessageListener {
@EJB private ProcessedMessageRepository processed;
@EJB private ShippingService shippingService;
public void onMessage(Message message) {
String eventId = message.getStringProperty("eventId");
if (processed.alreadyHandled(eventId)) {
return; // Idempotent Consumer Pattern
}
InvoiceCreated event = InvoiceCreated.fromJson(message.getBody(String.class));
shippingService.prepareDocuments(event.invoiceId());
processed.markHandled(eventId);
}
}
CREATE TABLE PROCESSED_MESSAGE (
EVENT_ID VARCHAR2(80) PRIMARY KEY,
CONSUMER_NAME VARCHAR2(80) NOT NULL,
PROCESSED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
BUSINESS_KEY VARCHAR2(80) NOT NULL
);
queue: BILLING.INVOICE.EVENTS
backoutQueue: BILLING.INVOICE.BOQ
deadLetterQueue: SYSTEM.DEAD.LETTER.QUEUE
alerting:
queueDepthWarning: 1000
oldestMessageAgeWarning: 15m
manual-action:
- pruefe Correlation-ID im Billing-Log
- pruefe PROCESSED_MESSAGE auf doppelte Event-ID
- entscheide Requeue oder fachliche Korrektur
| Aspekt | Beschreibung |
|---|---|
| Fachliches Risiko | Unklare Verantwortung fuer Ereignis fuehrt zu widerspruechlichen Entscheidungen zwischen Alt- und Neusystem. |
| Technisches Risiko | JMS und angrenzende Komponenten werden isoliert betrachtet; Laufzeitkopplung bleibt verborgen. |
| Betriebsrisiko | Fehlerkanal, Monitoring, Restart oder manuelle Klaerung sind nicht ausreichend dokumentiert. |
| Migrationsrisiko | Neue Architektur uebernimmt Daten oder Schnittstellen, ohne fachliche Invarianten und historische Sonderfaelle abzusichern. |
| Pattern | Einsatz in diesem System |
|---|---|
| Strangler Fig Pattern | Neue Funktionalitaet vor das Altsystem setzen und Altanteile schrittweise herausloesen. |
| Anti-Corruption Layer | Altbegriffe, technische Codes und Datenformate vom neuen Domänenmodell trennen. |
| Facade | Komplexe Legacy-Operationen hinter klaren fachlichen Use-Case-Methoden kapseln. |
| Adapter | Protokolle und Formate wie SOAP, MQ, Copybook, SQL oder File in Ports uebersetzen. |
| Golden Master Test | Bestehendes Verhalten mit Referenzdaten erfassen und gegen neue Implementierung vergleichen. |
Erstelle fuer eine Queue eine Betriebs- und Fachsicht: Wer sendet, wer empfaengt, welche Header existieren, welcher Idempotenzschluessel gilt, wann wird wiederholt, wann landet etwas in DLQ, wer entscheidet ueber Requeue?