Transactional Client
Business ↔ messaging atomicity — the "message sent if and only if DB committed" guarantee.
Problem
An app processes an inbound PO: (1) writes to DB, (2) sends an outbound 855 ACK. What to do if (1) succeeds but (2) fails? Retrying (1) recreates the PO. The partner waits for the ACK in vain.
Forces
- An atomic transaction between messaging and DB requires 2PC or an alternative pattern.
- 2PC is slow and brittle.
- Doing nothing causes inconsistencies observed in production.
- The Outbox pattern (modern alternative) solves without 2PC but adds latency.
Solution
Two options: (a) XA / 2PC protocol between broker and DB, rarely used in practice due to cost; (b) Outbox pattern: the DB transaction includes writing the message to an "outbox" table, an async worker reads this table and publishes to the broker. Transactional Client guarantees message and DB-state are consistent — both or neither.
EDI implementation
In modern EDI, the dominant implementation is Outbox: INSERT INTO outbox_table (msg_id, payload, status="pending") in the same transaction as INSERT INTO orders. A Debezium / CDC worker reads the table, publishes to Kafka, marks status="sent". Guarantees business+messaging atomicity without 2PC.
Anti-patterns
- Send-then-DB (sending before committing) — double-processing risk if commit fails.
- DB-then-send without Outbox — message-loss risk if the app crashes between.
- 2PC between Kafka and Postgres — operational cost too high for EDI volumes.
Related patterns
- Guaranteed Delivery — broker-side guarantee.
- Idempotency — protects against duplicates.
Sources
- Hohpe G., Woolf B. — EIP, Transactional Client (p. 484). www.enterpriseintegrationpatterns.com/patterns/messaging/TransactionalClient.html
- Microservices.io — Transactional Outbox. microservices.io/patterns/data/transactional-outbox.html