Enterprise Integration Patterns: The Working Vocabulary
The pattern language from Hohpe and Woolf's Enterprise Integration Patterns (2003) is old enough to drink, and it remains the working vocabulary of system integration — the ESBs that once claimed to embody it died, but the patterns moved into brokers, stream processors, and plain services. This page is the subset that earns daily use in a commerce + supply-chain stack, each with the concrete place it shows up. Fluency here is what makes integration design fast: you recognize the situation, name the pattern, and move on.
Channels and Messages
- Point-to-point channel — one consumer group processes each message once: work queues. Print this shipping label; exactly one worker should do it.
- Publish-subscribe channel — every subscriber gets every event. OrderShipped fans out to email notifications, the ERP invoice poster, and analytics simultaneously — none knows about the others.
- Dead-letter channel (DLQ) — where messages go after retries are exhausted, instead of blocking the queue or vanishing. The DLQ is only half the pattern; the other half is a human-facing process that drains it. An unmonitored DLQ is silent data loss with extra steps.
- Invalid message channel — the DLQ's sibling for messages that are malformed rather than unprocessable; keeping them separate makes triage instant (bug in the producer vs bug in the consumer).
- Message expiration / TTL — some messages rot: a stock-level update from an hour ago must not overwrite one from a minute ago. Expire or version them (see resequencing below).
Routing
- Content-based router — examine the message, choose the destination. Orders containing hazmat SKUs route to the special-handling queue; oversized items to the freight flow; everything else to standard pick.
- Splitter / Aggregator — break a composite message into parts, process independently, recombine on a correlation key. A five-line order splits into per-warehouse shipments; the aggregator reassembles shipment confirmations to decide when the order is fully shipped. The aggregator is the hard half — it holds state and needs a completeness rule and a timeout.
- Scatter-gather — ask several parties, collect answers. Quote shipping across three carriers, take the cheapest that meets the SLA.
- Resequencer — restore order when the transport doesn't guarantee it. In practice, prefer making consumers order-tolerant (version numbers on stock updates: ignore anything older than what you have) over true resequencing.
- Message translator — convert between two systems' formats at the boundary, so neither leaks its schema into the other.
- Canonical data model — with N systems, translate each to one shared format (N translators) instead of every pair (N²). Worth it from roughly the third system onward. The canonical
Order, SKU, Shipment need owning like code — versioned, reviewed, documented. - Content enricher — add data the source didn't have. The bare storefront order gets warehouse-assignment and carrier-service fields added before the WMS sees it.
- Claim check — pass a reference, not the payload, when messages would be huge. The message carries an object-store key to the 40MB product-image batch, not the bytes.
Endpoints
- Idempotent receiver — the load-bearing pattern of the whole catalog. Every serious transport delivers at-least-once, so every consumer must make duplicates harmless: dedupe on a message/business key, or make the operation naturally idempotent ("set stock to 41" not "decrement by 1"). Design every consumer this way and a whole class of incidents disappears. See ExactlyOnceAndDeliveryGuarantees for why the transport can't do this for you.
- Competing consumers — scale a queue by adding workers; the broker load-balances. The reason point-to-point queues scale trivially.
- Polling vs event-driven consumer — polling is simpler and self-healing (misses are picked up next cycle) but adds latency; event-driven is immediate but needs the reliability patterns above. Legitimate answer at small scale: poll everything on a cron, and graduate the flows that need speed.
- Correlation identifier — the id that ties a reply, event, or log line back to the originating request/order, carried through every hop. Also the backbone of tracing an order across five systems at 2am.
- Request-reply over messaging — possible (reply-to queues, correlation ids) but usually a smell in commerce flows: if you need an immediate answer, make a synchronous call; if you don't, design for fire-and-forget.
Reliability
- Guaranteed delivery + retry with backoff + DLQ — the standard consumer ladder: retry transient failures with exponential backoff, dead-letter the persistent ones, alert on DLQ depth.
- Transactional outbox — how a database write and a published event happen atomically (write both in one local transaction; a relay publishes from the outbox table). The fix for the dual-write problem, detailed in DataConsistencyAndSyncPatterns.
- Compensating action — the async world's rollback: you can't un-send a message, so you send the inverse (void the payment, restock the allocation). The building block of OrderOrchestrationAndSagas.
What Died with the ESB
The 2000s ESB promised these patterns as products — centralized, GUI-configured, vendor-locked — and became the bottleneck and single point of failure of a generation of IT estates. The lesson wasn't that the patterns were wrong; it's that smart endpoints and dumb pipes won: keep the broker simple (IntegrationBackboneChoices), put translation and routing logic in owned, versioned, testable code at the edges (IntegrationMiddlewareAndTooling — Apache Camel is these patterns as a library rather than a product).
See Also