Regional order routing
Acme sells into three regions and runs a warehouse in each: Europe (EMEA), the Americas (AMER), and Asia-Pacific (APAC). Every warehouse has its own fulfilment system, and the rule is simple. An order is fulfilled by the warehouse for its region. An order placed in Hamburg ships from the EMEA warehouse, never from Dallas.
So every incoming order has to be read, matched to its region, and handed to that region’s system. And sometimes an order will arrive carrying a region nobody recognises, a typo or a brand-new sales territory. That one must be set aside for a person, never guessed at and shipped to the wrong continent.
This is a textbook content-based router: the message itself carries the value that decides where it goes. Here is how Art2link ESB builds it.
Every order arrives as a canonical Order on the bus (the shared message backbone). The order already has a region field in its payload, but a field buried in the body is not something the bus can route on directly. So we promote it: we lift region into a named token, {{Promoted.Order.Region}}, that subscriptions can read without parsing the body. You promote only what you route on, nothing more.
Each region then gets its own send port, an API Caller pointed at that region’s fulfilment endpoint, and each port subscribes to a single value:
{{Promoted.Order.Region}} == "EMEA"
{{Promoted.Order.Region}} == "AMER"
{{Promoted.Order.Region}} == "APAC"
Because the promoted token names its message type, each expression already restricts itself to Order messages. Opening a fourth region is a new send port with a new value, the storefront and the existing ports never change.
When it fails. Two distinct failures here. An order whose Region matches no subscription is held in the Suspended state, the built-in dead-letter channel, the moment a typo or a new sales territory produces "UK&I"; nothing ships from the wrong continent and nothing is dropped. A delivery failure against a regional endpoint is the usual story: exception type FulfilCallFailed on each API port, one O365 Mail operations subscription, replay from tracking when the endpoint recovers.
{{Message.MessageType}} == "FulfilCallFailed"
Build it, step by step. The steps run in dependency order: every object is created before the object that selects it.
Under the application’s Message types, create the two types this flow routes on. A message type is a name plus a format, no schema required:
| Name | Format | Purpose |
|---|---|---|
| Order | JSON | the canonical order the router fans across the regions |
| FulfilCallFailed | JSON | the exception type a failed fulfilment call re-publishes under, payload intact (Step 5) |
Promotions live on the message type, so add it while you are here: lift the region field into a named token the bus can route on. Subscriptions are plain strings, they bind {{…}} tokens but never evaluate a body path; from now on every published order exposes {{Promoted.Order.Region}} to subscriptions.
Promotion name : Region
Source : $.region (the canonical Order payload field)
Exposed as : {{Promoted.Order.Region}}The payload the Step 6 test posts, region field included:
{
"orderNumber": "SO-10515",
"region": "AMER",
"customer": { "id": "C-1148" },
"total": 740.00
}Five external parties, five credentials. Under the application’s Authentications, the dialog asks for a Name, the Adapter it pairs with, and a Definition, the credential shape that adapter offers, with the Application preset; the Definition decides the config section that follows.
| Setting | Value |
|---|---|
| Name | StorefrontToken |
| Adapter | API Listener |
| Definition | API Listener Token |
| Token (Partner Config) | the secret the storefront will present on every call |
Each region’s fulfilment API has its own endpoint and its own account, so create three credentials, one per regional port; they differ only in name and values. The Definition is whatever that region’s API demands: API Bearer Token (Login URL, Refresh Token URL, Token JSON Path, Refresh Token JSON Path, Username, Password) or API Basic Authentication (Username and Password).
| Setting | Value |
|---|---|
| Name | FulfilEMEAAuth / FulfilAMERAuth / FulfilAPACAuth |
| Adapter | API Caller |
| Definition | API Bearer Token or API Basic Authentication |
| Credential fields | that region’s account values, per the chosen Definition |
| Setting | Value |
|---|---|
| Name | O365Ops |
| Adapter | O365 Mail Sender |
| Definition | Microsoft Graph |
| Tenant Id / Client Id / Client Secret (Graph AuthConfig) | an app registration with Mail.Send granted |
If the online order intake build already lives in this Application, skip this step: its API Listener and WebOrderToOrder map are the intake. On an empty environment, stand up a minimal edge instead. This walkthrough starts where a canonical Order is already on the bus, so the test rig publishes one directly, no map. Create a receive port WebshopOrders. (The dropdowns can also create types and maps inline; building the dependencies first keeps each dialog a selection.)
| Setting | Value |
|---|---|
| Adapter | API Listener |
| Way | One |
| Authentication | StorefrontToken, from Step 2 |
| Message Type Fallback | Order, from Step 1 |
Declare the two matching headers that keep this Listener unique on the shared ingress (routing on a shared ingress covers the full rule):
| Header | Value | Why |
|---|---|---|
| App | AcmeOrders | the Application’s namespace, narrows the request to this Application |
| Purpose | OrderIntake | keeps this Listener unique if the App later gains a second one (e.g. a stock lookup) |
The Step 6 test then posts orders, one per region, straight to the shared ingress with those two headers.
Create three send ports on the API Caller, FulfilEMEA, FulfilAMER, FulfilAPAC, each with its region’s endpoint URL and its own Authentication, from Step 2. On every port, Method POST and Outbound map None: the canonical Order JSON passes as the request body unchanged, {{Message.Body}}.
| Port | Endpoint URL |
|---|---|
| FulfilEMEA | https://eu.fulfil.example/orders |
| FulfilAMER | https://us.fulfil.example/orders |
| FulfilAPAC | https://ap.fulfil.example/orders |
Each regional API Caller subscribes on its own value:
FulfilEMEA : {{Promoted.Order.Region}} == "EMEA" FulfilAMER : {{Promoted.Order.Region}} == "AMER" FulfilAPAC : {{Promoted.Order.Region}} == "APAC"
On all three fulfilment ports, set Exception Message Type, on the port’s General step, to FulfilCallFailed, from Step 1.
Create the OpsAlert O365 Mail port subscribing on {{Message.MessageType}} == "FulfilCallFailed":
| Setting | Value |
|---|---|
| Authentication | O365Ops, from Step 2 |
| From | noreply@acme.example |
| To | ops@acme.example |
| Subject | Regional fulfilment call failed |
For the no-match case, turn on Activity Notifications (On Error Only) for the Application so a suspended order emails someone too.
Everything you configure is live the moment you save it, there is nothing to deploy. Start the send ports first, then the receive ports.
Set Tracking to Enabled + Body on every port the flow touches, so you can walk each run step by step, with its message body, in tracking.
Post one order per region and confirm in tracking that each leaves through the right port.
Post an order whose canonical region is "XX" (on the full intake build, the storefront field ship_region carries it there) and confirm it suspends (the built-in dead-letter channel) rather than shipping from the wrong continent. Then Terminate the suspended message and post a corrected order.