Back to blog
Integration Engineeringintermediate

Lecture 8: Enterprise Integration Patterns

Understand the Enterprise Integration Patterns (EIP) catalogue: message channels, routing patterns, transformation patterns, and process coordination patterns including Saga and CQRS — with examples and when to apply each.

SystemForgeApril 18, 202610 min read
Enterprise Integration PatternsEIPMessage RoutingSagaCQRSFITech
Share:š•

Enterprise Integration Patterns (EIP) are a catalogue of proven solutions to recurring integration problems. Originally documented by Gregor Hohpe and Bobby Woolf in their 2003 book of the same name, the 65 patterns in the catalogue describe every fundamental mechanism needed to build messaging-based integration systems. This lecture covers the most important patterns and — critically — when to use each.


What Are Enterprise Integration Patterns?

Patterns are reusable solutions to known problems. In software architecture, a pattern is not code you copy — it is a description of a solution structure that you adapt to your context.

EIPs describe solutions to problems like:

  • How do I route a message to the right recipient based on its content?
  • How do I split a batch message into individual messages?
  • How do I collect responses from multiple systems and combine them?
  • How do I coordinate a multi-step business process without distributed transactions?

Why learn patterns instead of just technology? Because the patterns are technology-independent. Whether you use Apache Camel, MuleSoft, Azure Logic Apps, or Node.js — the underlying patterns are the same. Knowing the pattern name also enables precise communication with colleagues and documentation.


Pattern Category 1: Message Channels

A Message Channel is the pipe through which messages flow. Different channel types serve different purposes.

Point-to-Point Channel

One message, one receiver. The channel guarantees that each message is consumed by exactly one consumer.

Producer ──► [Queue] ──► Consumer
                    (one of N consumers gets each message)

Use: task queues, work distribution, any case where each message should be processed once.

Publish-Subscribe Channel

One message, multiple receivers. Each subscriber gets its own copy.

Producer ──► [Topic] ──► Subscriber A
                     ──► Subscriber B
                     ──► Subscriber C

Use: event notifications, broadcasting state changes, feeding multiple downstream systems.

Dead Letter Channel

Where failed messages go after exhausting retries. Every production channel should have one.

Queue ──► Consumer (fails repeatedly)
             ↓ (after max retries)
         [Dead Letter Queue] ──► Operations team alert

Datatype Channel

A channel dedicated to a specific message type. Consumers subscribe to channels for the message types they handle.

[order.placed channel]    ──► Inventory, Billing
[customer.updated channel] ──► CRM Sync, Notification
[payment.failed channel]  ──► Finance, Customer Support

Pattern Category 2: Message Construction

Command Message

A message that tells the receiver to perform an action. The sender expects the action to happen.

JSON
{ "type": "SendEmail", "to": "user@example.com", "subject": "Order confirmed", "body": "..." }

Event Message

A message that announces something that happened. The sender does not know or care what consumers do with it.

JSON
{ "type": "OrderPlaced", "orderId": "ORD-1234", "placedAt": "2026-04-18T10:30:00Z" }

Document Message

A message that transfers a data document — no command, no event. The receiver stores or processes the document.

JSON
{ "type": "InvoiceDocument", "invoice": { "invoiceId": "INV-5678", ... } }

Request-Reply

A command message paired with a reply address. The sender specifies where to send the result:

JSON
{
  "type": "ValidateAddress",
  "address": { ... },
  "replyTo": "validation.results.queue",
  "correlationId": "abc-123"
}

Pattern Category 3: Message Routing

Content-Based Router

Routes a message to different channels based on its content.

incoming message
  ā”œā”€ā”€ if region = "EU" → EU-orders queue
  ā”œā”€ā”€ if region = "US" → US-orders queue
  └── if region = "APAC" → APAC-orders queue

Use case: route orders to regional fulfilment centres; route support tickets by product category.

Implementation consideration: keep routing logic simple and fast. Complex routing rules that require database lookups slow the entire pipeline.

Message Filter

Passes only messages that meet a condition; discards the rest.

all order events → [Filter: amount > 10000] → high-value-orders queue

Use case: a downstream system only cares about a subset of events from a busy topic.

Splitter

Takes one message containing multiple items and splits it into individual messages — one per item.

batch of 500 customer records
  → [Splitter]
    → customer record 1
    → customer record 2
    → ...
    → customer record 500

Use case: process a batch file one record at a time; fan out an order's line items to inventory checks.

Aggregator

Collects related messages and combines them once a completion condition is met.

items for order 1234:
  item A arrives → buffer
  item B arrives → buffer
  item C arrives → buffer
  (all 3 items received) → [Aggregator] → complete order 1234

Completion conditions:

  • All expected messages received (when the count is known)
  • Timeout reached (emit whatever has been collected)
  • A "complete" signal message arrives

Use case: collect all line items before sending the complete order to the warehouse; wait for all sub-task responses before proceeding.

Resequencer

Collects out-of-order messages and reorders them before delivering to the consumer.

arrives: msg-3, msg-1, msg-4, msg-2
→ [Resequencer]
→ delivers: msg-1, msg-2, msg-3, msg-4

Use case: ordered processing when the transport does not guarantee order (e.g., multiple parallel consumers, then ordered delivery to a legacy system).

Scatter-Gather

Sends the same request to multiple receivers simultaneously, collects their responses, and aggregates into a single reply.

price-check request
  → [Scatter] → Supplier A → price response A
              → Supplier B → price response B
              → Supplier C → price response C
  ← [Gather] → lowest price result

Use case: price comparison, parallel data enrichment, multi-system validation.


Pattern Category 4: Message Transformation

Message Translator

Converts a message from one format or schema to another.

Source format (SAP IDoc) → [Translator] → Target format (JSON REST payload)

This is one of the most common integration operations. Every system has its own data model; the translator maps between them.

Envelope Wrapper / Unwrapper

Wraps a message in an envelope with metadata for routing and tracking, then strips the envelope at the destination.

payload
  → [Wrapper]: add correlation-id, timestamp, source, target
  → route through integration layer
  → [Unwrapper]: strip envelope, deliver payload to target

Claim Check

When a message payload is too large to pass through the broker efficiently, store the payload in external storage and send a reference (the "claim check") instead.

large payload (10MB order document)
  → [Store in blob storage]
  → send message: { "claimCheck": "storage://orders/ORD-1234.json" }
  
Consumer receives message
  → fetches full payload from storage using claim check

Use case: large documents, binary files, images in integration flows.

Normaliser

Routes messages of different formats to appropriate translators so all consumers receive a consistent format.

SAP IDoc order ──►
REST JSON order ──► [Normaliser] → canonical order format
CSV order batch ──►

Pattern Category 5: System Management

Control Bus

A separate channel for control messages — start, stop, reconfigure, health check — distinct from the data channel.

Data channel: actual business messages
Control channel: health checks, monitoring commands, configuration updates

Message History

Each component adds its identity to the message header as it passes through. Enables tracing the full route a message took.

message.history: [order-service, integration-layer, transformation-service, warehouse-api]

Message Store

Store a copy of every message that flows through the integration layer. Enables replay, debugging, and audit.


Saga Pattern

The Saga pattern manages distributed transactions across multiple services when there is no distributed lock available.

A saga is a sequence of local transactions. Each transaction updates one service's data and publishes an event triggering the next transaction. If any step fails, compensating transactions undo the previous steps.

Example: Order Fulfilment Saga

Step 1: Reserve inventory    → success: publish InventoryReserved
Step 2: Charge payment       → success: publish PaymentCharged
Step 3: Book shipment        → success: publish ShipmentBooked

If Step 3 fails:
  Compensate Step 2: Refund payment
  Compensate Step 1: Release inventory reservation

Two Saga Coordination Styles

Choreography: each service listens for events and decides its next action. No central coordinator.

OrderPlaced → [Inventory Service] → InventoryReserved → [Payment Service] → ...

Orchestration: a central saga orchestrator sends commands to each service and waits for results.

Orchestrator → command: ReserveInventory → [Inventory] → InventoryReserved
Orchestrator → command: ChargePayment   → [Payment]   → PaymentCharged

When to use choreography: loosely coupled services where adding steps should not require changing existing services.

When to use orchestration: when the workflow must be visible, traceable, and centrally managed (financial transactions, compliance-critical workflows).


CQRS (Command Query Responsibility Segregation)

CQRS separates the write model (commands that change state) from the read model (queries that return data).

Write side:
  Command → validate → update write model → emit event

Read side:
  Event → update read model (optimised for query)
  Query → read model returns data

Why separate them?

  • Write models enforce business rules and ensure consistency
  • Read models are optimised for query patterns — denormalised, pre-joined, cached
  • Read models can be rebuilt from events at any time if they become inconsistent or need a new shape
  • Read models can be scaled independently from write models

CQRS in integration: when a system receives events from an integration, it builds a local read model from those events rather than querying the source system on demand.


Idempotent Receiver

Every message consumer must be designed as an idempotent receiver — processing the same message twice must produce the same result as processing it once.

Why: at-least-once delivery means duplicates happen. Network timeouts can cause the sender to retry a message the receiver already processed.

Implementation:

On receive message:
  1. Check if message ID exists in processed-messages table
  2. If yes: return success (already processed, no-op)
  3. If no: process message, then insert message ID into table

The processed-messages table needs a TTL — old entries can be deleted after the retry window closes (e.g., 24 hours).


Pattern Reference Summary

| Pattern | Problem it solves | |---------|-------------------| | Point-to-Point Channel | Route message to exactly one consumer | | Pub-Sub Channel | Deliver message to all interested consumers | | Dead Letter Channel | Store unprocessable messages for investigation | | Content-Based Router | Route based on message content | | Message Filter | Discard messages that don't match a condition | | Splitter | Break batch into individual messages | | Aggregator | Collect related messages into one | | Scatter-Gather | Parallel multi-target request with aggregated response | | Message Translator | Convert between data formats | | Claim Check | Handle large payloads by reference | | Saga | Coordinate distributed transactions with compensation | | CQRS | Separate write model from read model | | Idempotent Receiver | Safe handling of duplicate message delivery |


Lecture 8 Summary

  • Enterprise Integration Patterns are a technology-independent catalogue of solutions to recurring messaging and integration problems.
  • Channel patterns define the communication topology: point-to-point (one consumer) vs. pub-sub (all subscribers).
  • Routing patterns — content-based router, filter, splitter, aggregator, scatter-gather — are the building blocks of any complex integration flow.
  • Transformation patterns — translator, normaliser, claim check — handle the challenge that every system speaks a different data language.
  • The Saga pattern replaces distributed transactions with a sequence of local transactions and compensating actions.
  • CQRS separates the write model (consistency, business rules) from the read model (optimised for queries, independently scalable).
  • Every consumer must be an idempotent receiver — duplicates are a normal condition in at-least-once delivery systems.

Next: Deep Dive — Messaging Systems

Enjoyed this article?

Explore the Integration Engineering learning path for more.

Found this helpful?

Share:š•

Leave a comment

Have a question, correction, or just found this helpful? Leave a note below.