Integration patterns in Mule: you already use them, you just don't know their names
- By Bruno Souza
- Integration
- 11 min read
On this page
Code
When you put a Scatter-Gather in a Mule flow, you are using a pattern described in 2003, under the same name, in the book Enterprise Integration Patterns by Gregor Hohpe and Bobby Woolf. The same goes for Choice, For Each, Transform Message and the queues, message exchanges and dead letter queue of Anypoint MQ. They are integration patterns built into components, and you can use every one of them for years without knowing they are patterns. This article is for developers who already build flows in Mule 4 and want to know what this vocabulary adds: the problem each component solves, what it costs and when you can do without it.
A 2003 book that still gives things their names
The book collects 65 patterns for designing message-based integration without being tied to any product (Hohpe). Each pattern has a name, an icon, a problem phrased as a question, and a solution. The Idempotent Receiver starts from the question of how a receiver should deal with duplicate messages. The Dead Letter Channel asks what the messaging system does with a message it cannot deliver (Idempotent Receiver, Dead Letter Channel).
The preface is dated September 2003 (Pearson sample), and the references belong to that era: the Dead Letter Channel page cites IBM’s MQSeries and a book on JMS. The products have changed; the questions have not. A duplicate message on a JMS queue and a duplicate event on Anypoint MQ call for the same decision: what to do when the same thing arrives twice.
The patterns already inside the components
| Mule component | Pattern in the book | The problem it solves |
|---|---|---|
| Anypoint MQ queue | Make sure a single receiver consumes each message | |
| Scatter-Gather | Send the same message to several destinations and combine the replies into one | |
| Choice | Send each message to the right destination based on its content | |
| For Each | Process a message that carries several items, one item at a time | |
| Transform Message | Let systems with different formats exchange data | |
| Anypoint MQ message exchange | Deliver a copy of the event to every interested party | |
| Anypoint MQ dead letter queue | Move a message that cannot be delivered out of the way | |
| Idempotent Message Validator | Receive the same message twice without doubling the effect |
The Mule documentation describes each of these components by what it does. Scatter-Gather runs its routes in parallel and aggregates the result (Scatter-Gather). Choice runs only the first route whose expression is true, or the default route (Choice). For Each splits the payload into elements and processes them one by one (For Each). Transform converts the input data into another structure or format (Transform). In Anypoint MQ, a message exchange sends each message, by default, to every queue bound to it (message exchanges); despite the name, it has nothing to do with Anypoint Exchange, where assets live. Each queue can have its own dead letter queue (queues). The book’s pages describe the same mechanisms starting from the problem (Scatter-Gather, Content-Based Router, Splitter, Message Translator, Publish-Subscribe Channel).
The name also tells you what the component is made of. In the book, Scatter-Gather sends the request to several recipients, through a Recipient List or a publish-subscribe channel, and uses an Aggregator to combine the replies into one (Scatter-Gather). That is two patterns in one component, and the second raises a question the first does not have: what to do when one of the replies fails. Mule has a specific answer. It waits for every route to finish and, if any of them failed, throws MULE:COMPOSITE_ROUTING, carrying the errors and also the results of the routes that succeeded (Scatter-Gather). The error handler for it is where someone decides whether a partial response is good enough.
An Anypoint MQ queue is a Point-to-Point Channel: each message is consumed by a single receiver (Point-to-Point Channel). Replacing that queue with an HTTP call changes more than the transport. With HTTP, if the receiver is down, the call fails immediately and resending becomes the caller’s problem. With Anypoint MQ, the message stays on the broker until it is consumed, for the time to live configured on the queue, which can be at most two weeks (queues).
The VM Connector has queues too, and that causes confusion. It is not a broker: its queues live inside the Mule runtime and are meant for communication within one application or between applications on the same Mule runtime (VM Connector). Persistent VM queues are not even available on CloudHub 2.0 or Runtime Fabric. A VM queue is no substitute for an Anypoint MQ queue: if the message has to wait for a receiver outside the application, the broker is what holds on to it.
The correlationId you see in every Mule log is a relative of the Correlation Identifier. In the book, the ID goes out with the request and comes back with the reply, so the party that receives the reply knows which request it belongs to (Correlation Identifier). In Mule, the ID follows the request from one application to the next. The HTTP Listener reads the X-CORRELATION-ID header, or generates an ID when the header is missing, and HTTP Request has a Send Correlation Id option that defaults to AUTO (Correlation ID, HTTP Connector). In an MUnit test on Mule 4.9, where one flow calls another over HTTP with that option left at its default, the called flow received the caller’s ID in the header. Between two applications the path is the same header, so the logs of both show the same ID, and you can follow the request end to end. Both uses tie together messages from the same conversation: the book’s looks at the reply, Mule’s at the path.
What changes when you know the name
The Idempotent Message Validator makes the difference clear. It lets only messages with an ID it has not seen yet continue through the flow and throws MULE:DUPLICATE_MESSAGE for repeats (Idempotent Message Validator). In its minimal form, with no parameters, it looks like this (the syntax is the documentation’s):
<idempotent-message-validator doc:name="Idempotent Message Validator"/>
If you only know the component, you drop that line into the flow and move on. If you know the pattern, you start from the problem: the same message may arrive more than once, and the receiver has to produce the same effect whether it gets it once or twice (Idempotent Receiver). That definition leads to two questions the line above does not answer.
What identifies “the same message”? Without configuration, the ID is #[correlationId], and that ID was not made for this job. It gets it wrong in both directions. A client that resends the same order without the X-CORRELATION-ID header arrives with a new ID generated by Mule, and the duplicate gets through (Correlation ID). And the reverse: a Mule application that calls your API three times inside a For Each, with three different orders, sends the same ID on all three calls. In an MUnit test with the validator left unconfigured, the first call went through and the other two were rejected as duplicates. The right ID is a business key, such as the order number, set in idExpression.
How long should it remember? Without configuration, the IDs are kept in a non-persistent object store with a 5-minute expiry (validator parameters). A resend six minutes later, or after the application restarts, goes through as a new message. The right window depends on how long resends can take in your scenario, and the object store can be made persistent.
In both cases the component did exactly what the documentation says. The mistake was made by the person who configured it, who never asked what problem the configuration was supposed to solve.
The pattern doesn’t fail; the choice or the implementation does
I have never seen a pattern go wrong. The mistake lies with the person implementing it, as with the validator configured with nothing but its defaults. And there is the other side: the architect who designs something unnecessary, something that could have been simpler.
A hypothetical example of that second case: an order-created event consumed only by the billing system goes out through an Anypoint MQ message exchange with a single queue bound to it, and passes through an idempotent validator on the consumer that keeps the IDs in an object store. Each piece can be justified by the book, but two of them solve problems that do not exist here. There is a single interested party, so the message exchange distributes nothing. And the billing operation is already idempotent by order key, so the validator only adds a store that someone has to size. The queue’s dead letter queue is a different story: a failing message will always show up, whether there is one consumer or ten.
Knowing the name of the pattern also helps you see when you don’t need it:
- Publish-subscribe makes sense when there is, or soon will be, more than one consumer of the same event. With a single consumer, a queue is enough, and the message exchange becomes one more place to look for the message that went missing.
- An idempotent receiver with an object store makes sense when the target operation is not idempotent by nature. An upsert by business key can absorb the duplicate without a store to size, as long as the operation has no other side effects, such as sending an email or publishing an event.
- Scatter-Gather makes sense when the calls are independent and every reply matters. If the second call depends on the first, they cannot run in parallel. And because it waits for every route, one slow route holds up the whole response.
The patterns in an order scenario
In Mule, some of these patterns come built into components and connectors, and we often use them without realizing we are using a pattern. The series exists to put a name on what is already there, and the wiki is where each pattern gets explored in depth, with one page per pattern. Each page covers the problem, the book’s diagram of the pattern, a diagram of how it looks in Mule, an example and when not to use it. The catalog of integration patterns in Mule maps each component to its pattern and its page.
The example architecture
The diagram shows the scenario the pages use and where some of the patterns appear in it.
The orders API receives the order and publishes the order-created event to an Anypoint MQ message exchange, which delivers a copy to the queue of each interested party: payment, inventory and invoicing (publish-subscribe). The payment consumer may receive the same order more than once and must charge it only once (idempotent receiver). When the invoice consumer fails to acknowledge a message after the delivery attempts run out, Anypoint MQ moves it to the queue’s DLQ (dead letter channel); the default is 10 attempts (queues). The payment consumer calls the payment provider with an ID, the provider replies later through a callback carrying the same ID, and the payment confirmation uses that ID to match the reply to the request it came from (correlation identifier).
The parts that run inside Mule have a runnable example with an MUnit test in the mulesoft-patterns-examples repository, with no external systems. The parts that depend on the broker, such as the message exchange and the DLQ, appear in the wiki with the Anypoint MQ configuration explained from the documentation, because they do not run without a broker.
Before you open the wiki, try an exercise: open a Mule application you maintain, list the components from the table above that it uses and, for each one, say what problem it solves there.