Architektur
ArchitekturFortgeschritten

Das Outbox Pattern: Zuverlässige Events ohne verteilte Transaktionen

Daten speichern und ein Event veröffentlichen sind zwei Schreibvorgänge in zwei Systemen, und kein Retry macht sie atomar. Die Transactional Outbox schreibt das Event in derselben Datenbanktransaktion und liefert es danach aus. Schema, Polling vs. CDC mit Debezium, idempotente Consumer, Reihenfolge und Betrieb.

Titelbild: Das Outbox Pattern: Zuverlässige Events ohne verteilte Transaktionen
Inhalt

Jeder Service, der Daten besitzt, muss dem Rest des Systems irgendwann mitteilen, dass etwas passiert ist. Eine Bestellung geht ein, also müssen Billing, Versand und E-Mail davon erfahren. Der naheliegende Code sind zwei Zeilen: Bestellung speichern, dann OrderPlaced veröffentlichen.

Diese zwei Zeilen sind die häufigste Quelle für stillen Datenverlust in eventgetriebenen Systemen. Sie schreiben in zwei verschiedene Systeme, und nichts macht das Paar atomar. Das Outbox Pattern (Transactional Outbox) ist die Standardlösung: Schreiben Sie das Event in derselben Transaktion wie die Daten in Ihre eigene Datenbank und liefern Sie es danach aus. Dieser Leitfaden zeigt den Fehler, den es verhindert, das Schema, beide Wege der Auslieferung und die Teile, die die meisten Umsetzungen falsch machen: Duplikate, Reihenfolge und Aufräumen.

Das Outbox Pattern: Event mit den Daten committen, danach ausliefern.

Das Dual-Write-Problem

In eine Datenbank speichern und an einen Message Broker senden ist ein Dual Write. Es gibt nur drei mögliche Reihenfolgen, und alle drei scheitern.

Das Dual-Write-Problem: Commit und dann Absturz verliert das Event, Senden und dann Rollback erzeugt ein Phantom-Event, Senden innerhalb der Transaktion hält Locks und endet bei Timeout in einem unbekannten Zustand.
Jede Reihenfolge scheitert anders, und Retries reparieren keine davon.
  1. Erst committen, dann senden. Der Prozess stürzt nach dem Commit ab, wird neu deployt oder verliert das Netzwerk. Die Bestellung existiert, das Event nicht. Billing bucht nie ab, der Versand verschickt nie, und nirgends steht ein Fehler im Log, denn genau der Code, der es wiederholt hätte, ist gestorben.
  2. Erst senden, dann committen. Das Event geht raus, dann scheitert die Transaktion an einem Constraint oder Deadlock und rollt zurück. Consumer handeln jetzt auf Basis einer Bestellung, die nie existiert hat: ein Phantom-Event.
  3. Innerhalb der Transaktion senden. Der Broker ist nicht Teil Ihrer Datenbanktransaktion, das wirkt also nur sicherer. Zeilensperren bleiben gehalten, während Sie auf das Netzwerk warten, und bei einem Timeout wissen Sie nicht, ob die Nachricht rausging.

Die Lehrbuchantwort ist eine verteilte Transaktion (Two-Phase Commit, XA) über beide Systeme. In der Praxis nehmen die meisten Broker nicht an XA teil, und 2PC koppelt die Verfügbarkeit beider Systeme aneinander. Kafkas eigene Transaktionen umfassen Schreibvorgänge in Kafka-Topics, nicht in Ihre Datenbank. Kafka hat zwar einen optionalen Weg angenommen, mit dem ein Producer an einem extern koordinierten Two-Phase Commit teilnehmen kann (KIP-939), aber auch das ist kein XA: Ihre Anwendung wird zum Koordinator und muss den Transaktionszustand selbst speichern und wiederherstellen.

Der Ausweg: aufhören, zwei Schreibvorgänge zu haben.

Was das Outbox Pattern ist

Das Outbox Pattern speichert ausgehende Events in einer Outbox-Tabelle, in derselben Datenbanktransaktion wie die fachliche Änderung, und veröffentlicht sie danach über ein separates Relay. So veröffentlichen Microservices Events zuverlässig und mindestens einmal, ohne verteilte Transaktion.

Das Pattern, von Chris Richardson auf microservices.io katalogisiert, macht aus zwei Schreibvorgängen einen:

  1. Das Event als Zeile schreiben. Eine outbox-Tabelle liegt in derselben Datenbank wie Ihre Fachdaten.
  2. Dieselbe Transaktion nutzen. Fachliche Änderung und Outbox-Insert werden gemeinsam committet oder gemeinsam zurückgerollt. Das Event existiert genau dann, wenn die Änderung committet wurde.
  3. Danach weiterreichen. Ein separater Prozess liest committete Outbox-Zeilen und veröffentlicht sie an den Broker, mit Retries, bis der Broker bestätigt.
So funktioniert die Outbox: Die Bestellzeile und die Outbox-Zeile werden in einer Datenbanktransaktion geschrieben, ein Relay liest committete Outbox-Zeilen, veröffentlicht sie nach Aggregat-ID geschlüsselt an den Broker, und Consumer deduplizieren anhand der Event-ID.
Das Event teilt das Schicksal der Daten. Die Zustellung wird at-least-once, und Consumer deduplizieren.

Die Datenbanktransaktion, der Sie ohnehin vertrauen, ist jetzt der einzige atomare Schritt. Alles nach dem Commit ist eine Retry-Schleife, und Retry-Schleifen lassen sich leicht zuverlässig machen.

Es ist derselbe Baustein, der am Anfang einer zuverlässigen Webhook-Zustellung steht und hinter dem Versand von einer Million Benachrichtigungen ohne Absturz.

Schritt 1: die Outbox-Tabelle

Verwenden Sie die Spaltennamen, die Debezium standardmäßig erwartet. Sie kosten nichts und halten den Weg zu Change Data Capture offen.

outbox.sql
CREATE TABLE outbox (
id uuid PRIMARY KEY,
aggregatetype varchar(255) NOT NULL, -- bestimmt das Topic, z. B. "order"
aggregateid varchar(255) NOT NULL, -- Nachrichtenschlüssel, hält die Reihenfolge pro Aggregat
type varchar(255) NOT NULL, -- "OrderPlaced"
payload jsonb NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(),
published_at timestamptz -- nur für ein Polling-Relay
);
-- Das Polling-Relay betrachtet nur unveröffentlichte Zeilen.
CREATE INDEX outbox_unpublished ON outbox (created_at)
WHERE published_at IS NULL;

Die id ist die Identität des Events für sein ganzes Leben. Consumer erkennen daran Duplikate, also erzeugen Sie sie in der Anwendung, nicht im Relay.

Schritt 2: das Event in derselben Transaktion schreiben

place-order.sql
BEGIN;
INSERT INTO orders (id, customer_id, total) VALUES (42, 7, 99.00);
INSERT INTO outbox (id, aggregatetype, aggregateid, type, payload)
VALUES (gen_random_uuid(), 'order', '42', 'OrderPlaced',
'{"eventVersion": 1, "orderId": 42, "customerId": 7, "total": 99.00}');
COMMIT;

Zwei Details zählen mehr, als es scheint:

  • Der Payload ist ein Vertrag, kein Tabellen-Dump. Schreiben Sie das Event, das Consumer brauchen, mit expliziter Version, nicht SELECT * der Bestelltabelle. Ihre Tabelle wird sich ändern; das Event-Schema muss sich kompatibel weiterentwickeln.
  • Halten Sie die Transaktion kurz. Die Outbox fügt einen Insert hinzu. Sie darf keinen Netzwerkaufruf hinzufügen.

Schritt 3: die Zeilen weiterreichen

Es gibt zwei Wege, committete Zeilen aus der Tabelle zu holen. Richardson nennt sie Polling Publisher und Transaction Log Tailing.

Polling Publisher gegen Transaction Log Tailing: Polling fragt die Outbox-Tabelle mit SKIP LOCKED ab und markiert Zeilen als veröffentlicht; Log Tailing liest WAL oder Binlog mit Debezium und seinem EventRouter und veröffentlicht an ein Topic, geschlüsselt nach Aggregat-ID.
Mit Polling starten. Zu CDC wechseln, wenn Latenz, Last oder Reihenfolge es verlangen.

Variante A: Polling Publisher

Eine Schleife in Ihrem eigenen Service. Einen Batch beanspruchen, veröffentlichen, als erledigt markieren, alles in einer kurzen Transaktion:

relay-batch.sql
BEGIN;
SELECT id, aggregatetype, aggregateid, type, payload
FROM outbox
WHERE published_at IS NULL
ORDER BY created_at
LIMIT 100
FOR UPDATE SKIP LOCKED;
-- jede Zeile an den Broker senden und auf das Ack warten, dann:
UPDATE outbox SET published_at = now() WHERE id = ANY($published_ids);
COMMIT;

FOR UPDATE SKIP LOCKED (PostgreSQL ab 9.5, MySQL ab 8.0) lässt mehrere Relay-Instanzen laufen, ohne dass sie um dieselben Zeilen kämpfen. Damit kommen auch die zwei klassischen Fehler von Polling-Relays:

Der zweite Fehler ist die Reihenfolge. Mit mehreren Relays und SKIP LOCKED können zwei Events derselben Bestellung von verschiedenen Instanzen in beliebiger Reihenfolge veröffentlicht werden. Wenn das für Consumer zählt, und meistens tut es das, geben Sie jedem Aggregat genau einen Besitzer: ein einziges Relay oder Relays, die jeweils einen festen Bereich von hash(aggregateid) beanspruchen.

Polling bringt bis zu ein Intervall Latenz. In PostgreSQL senken Sie sie, ohne häufiger zu pollen: NOTIFY outbox in der schreibenden Transaktion, und das Relay lauscht per LISTEN. Benachrichtigungen werden erst beim Commit zugestellt, das Relay wacht also genau dann auf, wenn es etwas zu lesen gibt, und das Intervall wird zum Sicherheitsnetz.

Variante B: Transaction Log Tailing (CDC)

Statt die Tabelle abzufragen, lesen Sie das Transaktionslog der Datenbank: das WAL in PostgreSQL, das Binlog in MySQL. Inserts kommen in Commit-Reihenfolge an, ganz ohne Polling-Abfragen. Das übliche Werkzeug ist Debezium mit seinem Outbox Event Router:

outbox-connector.properties
connector.class=io.debezium.connector.postgresql.PostgresConnector
plugin.name=pgoutput
table.include.list=public.outbox
transforms=outbox
transforms.outbox.type=io.debezium.transforms.outbox.EventRouter
transforms.outbox.table.expand.json.payload=true
value.converter=org.apache.kafka.connect.json.JsonConverter

Mit den Standardspalten aus Schritt 1 veröffentlicht der Router jede Zeile an das Topic outbox.event.<aggregatetype>, nutzt aggregateid als Nachrichtenschlüssel, sodass alle Events eines Aggregats geordnet in einer Partition landen, und legt die Event-id zur Deduplizierung in einen Nachrichten-Header.

Zwei Dinge machen Log Tailing im großen Maßstab attraktiv:

  • Die Tabelle kann leer bleiben. Fügen Sie die Outbox-Zeile ein und löschen Sie sie in derselben Transaktion. Der Insert steht trotzdem im Log, Debezium veröffentlicht ihn, und der Router ignoriert Deletes. Kein Aufräumjob, kein Bloat.
  • Keine Abfragelast. Der Connector liest das Log, das die Datenbank ohnehin schreibt.

Und eine Sache verdient Respekt: In PostgreSQL nutzt CDC einen Replication Slot, und die Datenbank hält WAL dafür vor, bis der Connector es bestätigt. Ein Connector, der ein Wochenende ausfällt, kann die Platte füllen. Alarmieren Sie auf Slot-Lag und begrenzen Sie ihn mit max_slot_wal_keep_size (ab PostgreSQL 13).

At-least-once: Consumer idempotent machen

Die Outbox garantiert, dass ein Event nie verloren geht. Sie garantiert nicht, dass es genau einmal ankommt. Das Relay kann einen Batch veröffentlichen und abstürzen, bevor es published_at setzt. Nach dem Neustart veröffentlicht es dieselben Events erneut. Eine erneute Zustellung durch den Broker oder ein Consumer-Absturz vor dem Ack bewirken dasselbe.

Jeder Consumer muss also deduplizieren, und der robuste Weg ist eine Inbox: Die Event-ID wird in derselben Transaktion gespeichert wie die eigentliche Änderung.

consume-order-placed.sql
BEGIN;
INSERT INTO processed_events (event_id) VALUES ($event_id)
ON CONFLICT (event_id) DO NOTHING;
-- 0 Zeilen eingefügt: Duplikat, committen und bestätigen, ohne etwas zu tun.
INSERT INTO invoices (order_id, amount) VALUES (42, 99.00);
COMMIT;
-- die Nachricht erst nach dem Commit bestätigen

At-least-once-Zustellung plus idempotente Consumer ist das, was in der Praxis mit „exactly-once“ gemeint ist. Dieselbe Technik schützt HTTP-APIs; die Details stehen in Idempotenz in der Praxis.

Reihenfolge: was Sie versprechen können und was nicht

  • Pro Aggregat: ja. Schlüsseln Sie jede Nachricht nach aggregateid. Kafka hält die Reihenfolge innerhalb einer Partition, Google Pub/Sub hat Ordering Keys für denselben Zweck. Alle Events zu Bestellung 42 kommen in Commit-Reihenfolge an, solange ein einziges Relay diesen Schlüssel besitzt.
  • Global: nein. Events zu Bestellung 42 und Bestellung 17 können in beliebiger relativer Reihenfolge ankommen. Bauen Sie Consumer so, dass das in Ordnung ist.
  • Trotzdem absichern. Legen Sie eine Version oder Sequenznummer pro Aggregat in den Payload. Ein Consumer, der Version 5 nach Version 6 sieht, kann sie ignorieren, statt neueren Zustand zu überschreiben.

Die Outbox betreiben

Aufräumen. Ein Polling-Relay hinterlässt veröffentlichte Zeilen. Löschen Sie sie nach einem kurzen Audit-Fenster in Batches, oder partitionieren Sie die Tabelle bei hohem Volumen nach Tag und verwerfen alte Partitionen, was im Vergleich zu großen DELETE-Anweisungen und der Vacuum-Arbeit, die sie auslösen, nichts kostet. Mit CDC löschen Sie in derselben Transaktion, wie oben beschrieben.

Monitoring. Eine Metrik sagt fast alles: das Alter des ältesten unveröffentlichten Events. Mit Polling ist das now() - min(created_at) WHERE published_at IS NULL, mit CDC der Replikations-Lag. Alarmieren Sie, wenn es einige Minuten überschreitet; dieselbe Regel fängt ein totes Relay, einen Broker-Ausfall und ein Poison Event.

Poison Events. Ein Event, das der Broker ablehnt (zu groß, ungültiges Schema), blockiert die Warteschlange für seinen Schlüssel. Zählen Sie Versuche pro Zeile und verschieben Sie sie nach einem Limit in eine Dead-Letter-Tabelle mit Alarm, statt endlos zu wiederholen.

Tracing. Speichern Sie den Trace-Kontext in der Outbox-Zeile und stellen Sie ihn im Relay wieder her, damit ein Trace dem Request vom API-Aufruf über den Broker bis in jeden Consumer folgt. Debeziums Router unterstützt das über eine Tracing-Spalte.

Outbox-Implementierungen in Java, .NET, Go und MongoDB

Das Relay müssen Sie selten selbst schreiben. Das Pattern steckt in den Frameworks, die die meisten Teams ohnehin nutzen:

Outbox-Implementierungen in Java, .NET, Go und MongoDB
Stack Fertige Outbox Hinweis
Java, Spring Spring Modulith Event Publication Registry Laut eigener Doku fehlen der nativen Externalisierung fortgeschrittene Outbox-Funktionen; 2.1 bringt Namastack Outbox und JobRunr als Backends
Java Eventuate Tram Von Chris Richardson, dem Autor des Pattern-Katalogs
.NET (C#) MassTransit Transactional Outbox, NServiceBus Outbox, Wolverine, CAP MassTransit und NServiceBus deduplizieren auch auf Consumer-Seite
Go Watermill Forwarder Veröffentlicht in Ihrer Transaktion in eine SQL-Tabelle und leitet an den echten Broker weiter
Jede Sprache Debezium (Kafka Connect oder Debezium Server) Debezium Server sendet ohne Kafka an Google Pub/Sub, Amazon Kinesis und andere

MongoDB unterstützt Multi-Document-Transaktionen auf Replica Sets, die Outbox funktioniert also genauso: Bestellung und Outbox-Dokument in einer Session-Transaktion schreiben. Debezium hat einen eigenen MongoDB Outbox Event Router, weil der relationale nicht mit dem MongoDB-Connector funktioniert.

Cloud-Datenbanken haben oft ein Log, das Sie statt Polling lesen können: DynamoDB Streams nach einem TransactWriteItems, das Item und Event gemeinsam schreibt, oder Spanner Change Streams auf einer Outbox-Tabelle.

Outbox oder etwas anderes?

Outbox oder etwas anderes?
Ansatz Atomar mit Ihren Daten Zusätzliche Infrastruktur Trade-off
Erst committen, dann senden Nein Keine Verliert Events bei Absturz
Two-Phase Commit (XA) Ja XA-fähiger Broker Die meisten Broker unterstützen es nicht; koppelt Verfügbarkeit
Transactional Outbox Ja Ein Relay At-least-once, Consumer müssen deduplizieren
CDC auf Fachtabellen Ja CDC-Pipeline Ihr internes Schema wird zum öffentlichen Vertrag
Event Sourcing Ja, das Event ist der Zustand Event Store Ändert Ihr gesamtes Persistenzmodell

Die Outbox ist die Variante, die einen bewussten Event-Vertrag behält und nichts Exotischeres braucht als eine lokale Transaktion.

Wann Sie keine Outbox brauchen

  • Niemand außerhalb der Datenbank muss davon erfahren. Ist die Folgearbeit ein Job in einer Queue-Tabelle in derselben Datenbank, wird die Job-Zeile bereits atomar mit Ihren Daten committet. Sie haben eine Outbox ohne den Namen.
  • Sie nutzen bereits Event Sourcing. Das Anhängen an den Event Store ist die Zustandsänderung; veröffentlichen Sie aus dem Store.
  • Ein verlorenes Event ist wirklich egal. Analytics-Pings und Best-Effort-Cache-Warming vertragen den seltenen Verlust. Fachliche Fakten wie Bestellungen, Zahlungen und Berechtigungen nicht.

Das Pattern passt auch direkt dazu, wie eine Microservices-Architektur funktioniert: Services besitzen ihre Daten, und asynchrone Events sind der Weg, über den der Rest des Systems von Änderungen erfährt, ohne die Datenbank eines anderen Service zu lesen.

Das Fazit

Der Fehler, den die Outbox behebt, ist im Test unsichtbar und in Produktion unvermeidlich: ein Absturz zwischen „gespeichert“ und „veröffentlicht“. Die Lösung ist, das Event nicht mehr als zweiten Schreibvorgang zu behandeln. Committen Sie es mit den Daten, in der Transaktion, der Sie ohnehin vertrauen, und lassen Sie ein Relay es mit Retries zustellen.

Fangen Sie einfach an: eine Outbox-Tabelle mit Debezium-kompatiblen Spalten, ein Polling-Relay mit SKIP LOCKED, das unveröffentlichte Zeilen selektiert statt an der letzten ID vorbeizulesen, Consumer, die anhand der Event-ID deduplizieren, und ein Alarm auf das Alter des ältesten unveröffentlichten Events. Wechseln Sie zu Log Tailing, wenn Latenz oder Last es verlangen. Ihr Schema ist schon bereit.

Weiterlesen: Webhooks erklärt: Signatur, Retries und Idempotenz · Eine Million Benachrichtigungen versenden, ohne abzustürzen

Häufig gestellte Fragen

Was bedeutet Outbox Pattern?

Das Outbox Pattern, auch Transactional Outbox, ist ein Verfahren, um eine Datenbank zu ändern und zuverlässig ein Event zu veröffentlichen, ohne verteilte Transaktion. Der Service schreibt die fachliche Änderung und eine Event-Zeile in eine Outbox-Tabelle innerhalb derselben lokalen Datenbanktransaktion. Ein separater Relay-Prozess liest committete Outbox-Zeilen und veröffentlicht sie an einen Message Broker wie Kafka, Pub/Sub oder RabbitMQ, bis der Broker bestätigt. Weil beide Schreibvorgänge in einer Transaktion liegen, existiert das Event genau dann, wenn die Änderung committet wurde.

Warum nicht einfach nach dem Commit an den Broker senden?

Weil der Prozess zwischen Commit und Senden abstürzen, beendet werden oder das Netzwerk verlieren kann. Die Daten sind gespeichert, das Event ist weg, und niemand wiederholt es, weil genau der Code gestorben ist, der es wiederholen würde. Senden vor dem Commit hat das spiegelbildliche Problem: Rollt die Transaktion danach zurück, erhalten Consumer ein Event zu Daten, die nie existiert haben. Den Broker innerhalb der Transaktion aufzurufen hilft auch nicht, denn der Broker ist nicht Teil der Datenbanktransaktion, und nach einem Timeout wissen Sie nicht, ob die Nachricht rausging.

Liefert das Outbox Pattern exactly-once?

Nein, es garantiert at-least-once. Das Relay kann eine Nachricht veröffentlichen und abstürzen, bevor es die Zeile als gesendet markiert; nach dem Neustart veröffentlicht es dasselbe Event noch einmal. Die Lösung liegt beim Consumer: Jedes Event trägt eine eindeutige ID, und der Consumer speichert verarbeitete IDs, idealerweise in derselben Transaktion wie seine eigene Änderung, sodass ein Duplikat erkannt und übersprungen wird. At-least-once plus idempotente Consumer ergibt effektiv einmalige Verarbeitung.

Was ist das Inbox Pattern?

Das Inbox Pattern ist das Gegenstück der Outbox auf Consumer-Seite. Bevor ein Consumer ein Event anwendet, fügt er die Event-ID in derselben Transaktion wie seine eigene Änderung in eine Inbox-Tabelle (verarbeitete Events) ein. Ist die ID schon vorhanden, ist das Event ein Duplikat und wird übersprungen. Weil die Outbox mindestens einmal zustellt, macht erst eine Inbox oder eine andere idempotente Verarbeitung aus wiederholter Zustellung eine einzige Wirkung.

Wie hängen Outbox Pattern und Saga Pattern zusammen?

Eine Saga steuert einen Geschäftsprozess über mehrere Services als Folge lokaler Transaktionen, jeweils gefolgt von einem Event oder Command, das den nächsten Schritt auslöst. Jeder Schritt hat damit dasselbe Dual-Write-Problem: lokal committen, dann den nächsten Service informieren. Die Outbox ist der Weg, über den jeder Saga-Schritt diese Nachricht zuverlässig veröffentlicht. Saga und Outbox werden deshalb meist zusammen eingesetzt, nicht als Alternativen.

Polling oder CDC mit Debezium für das Outbox-Relay?

Beginnen Sie mit einem Polling Publisher, sofern Sie nicht ohnehin Kafka Connect betreiben. Polling ist Ihr eigener Code, braucht keine zusätzliche Infrastruktur und ist schnell genug, wenn ein Intervall von einigen hundert Millisekunden akzeptabel ist. Wechseln Sie zu Log Tailing mit Change Data Capture, etwa Debezium mit seinem Outbox Event Router, wenn Sie weniger Latenz, weniger Abfragelast oder strikte Commit-Reihenfolge bei hohem Volumen brauchen. CDC bringt einen Connector zum Betreiben mit und in PostgreSQL einen Replication Slot, der WAL zurückhält, solange der Connector steht.

Wie räume ich die Outbox-Tabelle auf?

Mit einem Polling-Relay löschen Sie Zeilen nach dem Veröffentlichen oder behalten sie für ein kurzes Audit-Fenster und löschen sie in Batches; bei hohem Volumen partitionieren Sie die Tabelle nach Zeit und verwerfen alte Partitionen, was weit günstiger ist als große DELETE-Anweisungen. Mit Debezium können Sie die Outbox-Zeile in derselben Transaktion einfügen und wieder löschen: Der Insert steht trotzdem im Transaktionslog, der Connector veröffentlicht ihn, und der Outbox Event Router ignoriert den Delete. Die Tabelle bleibt dann leer.

Welche Alternativen gibt es zum Outbox Pattern?

Two-Phase Commit über Datenbank und Broker ist die Lehrbuchantwort, aber die meisten Broker, Kafka eingeschlossen, nehmen nicht an XA-Transaktionen teil, und es koppelt die Verfügbarkeit beider Systeme. Event Sourcing macht den Event Store zur führenden Quelle, sodass es keinen zweiten Schreibvorgang gibt, ändert aber Ihr gesamtes Persistenzmodell. Änderungen per CDC direkt aus den Fachtabellen zu lesen vermeidet ebenfalls den Dual Write, macht aber Ihr internes Schema zum Vertrag. Die Outbox behält einen bewussten, versionierten Event-Vertrag und braucht nur eine lokale Transaktion.

Aus der Community

Diskussion auf Mastodon & Bluesky

Antworten auf diesen Artikel aus dem offenen Netz, live geladen. Keine Werbung, keine Tracking-Skripte.

Antworten werden geladen …
Auf Mastodon antwortenAuf Bluesky antworten

Antworte dort — es erscheint hier automatisch.

ENDE