Connecting a Fragmented Enterprise with MuleSoft API-Led Integration
A manufacturer where four systems each held part of the customer, and the three-layer diagram everyone had already drawn answered none of the questions that mattered.
ManufacturingThree tiers rather than a web of direct connections. At the bottom, four System APIs each speak for one source system in that system own vocabulary: customer and credit from the ERP, orders and commercial terms from the legacy order platform, stock and despatch from the warehouse, and relationship, case and account data from Salesforce. Above them a small Process layer owns the sequences that cross systems, the order in which calls are made and what is undone when a later step fails. On top sit the consumers, which in this estate are almost all systems and internal screens rather than bespoke channels, which is why no Experience layer was built. The rule that holds the shape is that a consumer never calls a System API directly for anything that touches more than one system, and a System API never learns a business rule.
Consumers
- Sales Cloud, showing order and despatch status on the account
- Service Cloud, opening a case with fulfilment context attached
- Warehouse operator screens
- Finance period-end extract
Consumers ask for outcomes and never sequence calls themselves. What must never leak in here: knowledge of which source system holds which field.
Process APIs
- Onboard a customer across ERP and Salesforce
- Accept an order and release it to fulfilment
- Return fulfilment status to sales and service
- Release a credit hold and resume the order
Orchestration, compensation when a later step fails, and mapping between four vocabularies of customer. What must never leak in here: a rule that Salesforce already enforces in validation, approval or Flow, and any process that calls only one System API.
System APIs
- Customer and credit, over the ERP
- Order and commercial terms, over the legacy order system
- Stock, pick and despatch, over the warehouse platform
- Relationship, case and account, over Salesforce
Field and record type mapping, upsert on an external identifier, pagination, retry and error translation. What must never leak in here: segment or region conditionals, credit thresholds, or anything a business owner could change their mind about.
A worked scenario showing how we approach this problem. The architecture and decisions are our real practice; it is not an account of one named customer.
The estate, as it actually presented
The brief in this situation is almost never "we need an integration platform". It is that a service agent cannot tell a caller where their order is.
Trace that back and the same shape appears in most manufacturers of this size. The ERP holds the legal entity, the credit position and the billing account, because that is what an auditor tests. A legacy order system, in several cases older than the ERP, holds the order header and the commercial terms negotiated against it. A warehouse platform holds stock, pick status and despatch. Salesforce holds the relationship, the opportunity and the case.
Four systems, four partial customer records, and nowhere the whole one exists.
Between them sat about a dozen direct connections, each built by a project that had closed. A nightly file from the order system into Salesforce. A callout from Salesforce into the ERP for credit, written by a developer who had since left. A middleware job nobody could open, moving despatch confirmations. Each was defensible when it was built. Together they meant a change at any endpoint required somebody to survey the others, and nobody could produce the list to survey.
The three-layer diagram had already been drawn before we arrived. It was on a wall. It was correct, and it had settled nothing, because the questions that actually decide whether the layering repays its cost are all in what the diagram leaves out.
What we designed, and why
System APIs scoped to capability, not to object
The first real decision is what one System API covers, and the instinct is almost always wrong in the same direction.
Asked to build System APIs, a team enumerates the objects and builds one each. Account API, Contact API, Order API, Shipment API. It looks tidy on a wiki page and it produces an estate nobody can use without stitching.
The problem surfaces with the first consumer. Creating a customer here is not one object. It is an account, at least one contact, a relationship between them, a billing account in the ERP and the credit terms attached to it. With one API per object, that consumer makes several calls in an order it has to know, holds partial state between them, and decides for itself what happens when the fourth fails after three succeeded. The distributed transaction problem has been pushed out of the integration layer into every application touching it, and each application solves it differently.
So each System API was scoped to a bounded capability in its source system: what a consumer needs to accomplish there, expressed in that system own terms. Customer and credit over the ERP. Order and commercial terms over the legacy order system. Stock, pick and despatch over the warehouse. Relationship, case and account over Salesforce. Inside that boundary the System API owns the ordering and the compensation. Outside it, consumers see one call.
The test we applied at review was blunt. If a consumer has to make three calls in sequence and unwind them itself when the second fails, the boundary is in the wrong place.
The symmetric error is rarer and worse: one API for the whole of Salesforce, sixty operations versioned as a single thing, so a change for the service team drags the sales team into regression testing. The right boundaries usually sit where the estate is already administered separately, which in this case was visible in who held the admin rights.
Where Salesforce-specific logic was not allowed to sit
This is the failure that costs the most and surfaces the latest, and it is the reason the platform stops paying for itself.
A System API is meant to be the one place that knows how to talk to a system, and nothing beyond that. The moment the Salesforce System API also knows that an order above a certain value needs a second approver, or that a customer in one region skips the credit check, it is not a System API. It is a business process with a connector attached.
Three consequences follow, and each was argued through with the team before the rule was accepted.
The API stops being reusable. The next consumer wants the same customer data without that approval behaviour. It cannot have it, so it either builds a second API over the same objects or asks for a flag. Flags accumulate until the parameter set encodes the differences between four consumers, at which point reuse has quietly become branching.
Cheap changes become expensive. A credit threshold moves, which is a short conversation in the business, and it now requires a change to a shared component that four consumers depend on, with all their regression testing attached.
Nobody can find the rule. Somebody looking for where the threshold lives will look in the ERP, then in Salesforce, then in the orchestration. They will not think to look inside the connectivity layer. Logic hidden a layer below where anyone expects it gets duplicated rather than found.
The line we held is short enough to put in a review checklist. A System API may know how the system works. It may not know how the business works. Field mapping, record type resolution, defaults required for a record to save at all, error translation and idempotent upsert on an external identifier are all fair. A conditional whose condition is a business term is not.
The Process layer, and the discipline that kept it thin
The logic evicted from the System layer has to live somewhere, and the Process layer is where it went. This is the layer most estates leave thinnest and then let sprawl.
Four Process APIs were built, and only four. Onboard a customer across the ERP and Salesforce. Accept an order and release it to fulfilment. Return fulfilment status to sales and service. Release a credit hold and resume the order. Each of those crosses systems, which is precisely why none of them belongs inside one.
Three things live here and nowhere else. Orchestration, meaning the order of calls and the dependencies between them. Compensation, meaning what gets undone when the third step fails after the first two committed, which is a business decision rather than a technical one and was signed off as such. Cross-system mapping, between four different ideas of what a customer is.
The discipline problem is that a Process API is a comfortable place to put anything at all. It is code, it deploys, it needs no Salesforce admin, and it escapes the reuse conversation entirely. Left alone it becomes a second, ungoverned home for business logic that quietly contradicts what is already configured in the org.
Two rules were written into the review gate and enforced.
A Process API calls at least two System APIs, or it is not orchestrating anything. One proposed process fronted only the warehouse. It was deleted rather than documented, and its caller went straight to the System API.
Logic the platform already enforces is not re-implemented here. Where a validation rule, an approval or a Flow already encodes a rule, the Process API calls Salesforce and lets Salesforce enforce it. Duplicating a rule produces two versions that agree right up until somebody changes one.
The review question we kept asking: what would break if this Process API were deleted and its callers went straight to the System APIs? If the answer is "not much", it should not exist. If it names a sequencing or compensation concern, it has earned its place.
Whether this estate needed Experience APIs
It did not, and saying so was one of the more useful conversations.
Experience APIs shape a response for one specific consumer: a mobile app on a poor connection, a partner portal that must see a deliberately reduced view, a public form. They are real and they earn their place under three conditions, all of which have to hold. The consumer needs a genuinely different shape rather than a subset. That shape will move on the consumer release schedule rather than yours. Shielding the layers beneath from that churn has value.
Every consumer in this estate was another system or an internal screen: Sales Cloud, Service Cloud, warehouse operator screens, a finance extract. None of them needed a bespoke payload that would churn independently. Building a layer of components to version, deploy and monitor for them would have bought a shape they could have accepted as it was.
Two layers was recorded as the architecture, with the condition that would trigger revisiting it written next to the decision: a customer-facing or partner-facing channel with its own release cadence.
Implementation
The sequence mattered more than the components.
The register first, before any design. Nobody could list what already connected to the org, so that list was assembled from evidence rather than from interviews: integration users and their entitlements, connected apps, outbound endpoints, scheduled jobs. The difference between that list and the one people gave from memory was treated as the finding. You cannot choose boundaries against a requirement you have not established.
Then attribute ownership. Which system is authoritative for legal entity name, for billing address, for order status, for despatch date. Settled per attribute rather than per system, because framed as a system-against-system argument it does not resolve, and framed per attribute it resolves in an afternoon. Every consuming system holds those attributes read-only unless a write-back exception is written down with an approver against it.
Then the four System APIs, one at a time. Starting with the warehouse, because it had the cleanest shape and the fewest owners, and finishing with the legacy order system, which had the most surprises in it. Sequencing this way means a defect is attributable to one change rather than four.
Then the Process APIs, then the consumers, then the old connections retired. Retirement was a named task with a date, not an assumption. Direct connections left running alongside a new API do not decay quietly; they become the path somebody uses under deadline pressure, and the estate ends up carrying both.
The decisions that were contested
Not putting the credit rule in the ERP System API. The developer who proposed it was right that it would have been faster, and right that it was only one conditional. The argument that carried was the second consumer: the moment finance wanted the same customer view without the credit behaviour, that one conditional becomes a flag, and flags are how reuse turns into branching. It went into the Process layer, where anyone looking for it will find it.
Deleting a Process API that had already been designed. It fronted a single system and would have shipped happily. Removing it meant telling a team that a fortnight of design was not going to be built. The rule is only worth having if it survives the first time it is inconvenient.
Keeping the legacy order system rather than absorbing it. The tidier architecture moves order management into Salesforce and removes a System API entirely. The reason not to, at this stage, was that the commercial terms held in that system had years of accumulated behaviour nobody had documented, and migrating them under an integration programme would have meant two hard projects wearing one budget. The System API buys the time to do that separately and deliberately.
Refusing to build the customer 360 screen in phase one. There was appetite for a single view assembled live across four systems. We argued to wait, because a view assembled across four systems whose ownership was still being agreed would have shown four versions of the truth on one screen, and taught everyone to distrust it.
What changes
The outcomes worth claiming here are operational, and they follow from the design rather than from effort.
| Before | After | What made the difference |
|---|---|---|
| Around a dozen direct connections, no current list | One layered estate with a maintained interface register | The register was built from evidence before any design started |
| A change at one endpoint required surveying the others | Endpoints change behind a stable System API contract | The System API is the only thing that knows the source system shape |
| Consumers sequenced multi-step writes themselves | Consumers make one call and the layer owns ordering | Boundaries drawn around bounded capabilities, not objects |
| Business rules embedded wherever the connection was written | Rules sit in the Process layer or in the platform that already enforces them | The rule that a System API may not know how the business works |
| Order status answered by a human opening three systems | Order status returned as one composed response | A Process API owns the sequence across four vocabularies |
The second-order effect is the one leaders notice. The conversation in a change review stops being "what might this break" and becomes "which contract does this touch", which has an answer somebody can look up.
Why reuse fails, and what we did about it
Layering costs more than not layering: more components, more deployments, more tests. That cost is repaid only when the second and third consumers use what the first one built, and across a lot of estates the repayment never arrives. It fails for reasons that are predictable and mostly not technical.
The first consumer shapes the contract around itself. Built to a deadline for one project, the API carries that project assumptions in its field set and its granularity. The second consumer finds it nearly right, and nearly right is frequently more expensive than starting again. The counter is half a day sketching the second and third use case while designing the first, which is the cheapest architectural insurance available.
Nobody knows it exists. An API that is not catalogued, not documented with an example a developer can run, and not findable outside the original project gets rebuilt by the next team, who are genuinely unaware. A catalogue somebody outside the integration team can browse is not documentation overhead; it is the mechanism.
The second consumer cannot get a change made. They need one extra field. The owning team has its own backlog and release train. Six weeks of waiting against two days of forking is not a close decision under delivery pressure, and the fork is where the estate fragments again.
Versioning was never designed. Without a policy agreed before the first release, every change is potentially breaking, consumers freeze on the version they integrated against, and the API ossifies into as many variants as it has callers.
None of that is platform configuration, and none of it is something Anypoint does for you. It is the part of the programme the tooling cannot carry, and it decides whether the layering was worth building at all.
What we would tell you before starting
Build the register before you draw anything. If you cannot produce a current list of what connects to your org, every boundary you draw is being drawn blind, and the diagram on the wall will be accurate and useless in the same way the last one was.
Settle attribute ownership before the first System API, not during it. It is two workshops, no tooling, and it pre-empts a category of argument that otherwise recurs at every design review for the life of the estate.
Then be honest about whether the platform is warranted. A single point-to-point interface does not justify a platform. One nightly extract from one system into Salesforce, with no second consumer in prospect, is a nightly extract, and layering it buys a runtime, a pipeline, a monitoring surface and a skill set somebody has to keep current in exchange for reuse that will never be claimed. The signals that it is not yet warranted are worth stating plainly: one or two interfaces between the same pair of systems, no identified second consumer, and no team with capacity to operate another runtime, which is the constraint most often left unstated.
The signals that it is warranted are the ones this estate had: a rising count of direct connections where a change at one endpoint requires surveying the others, the same data pulled from the same source by three teams who each wrote their own version, and a genuine need for policy, access control and consumption visibility as products rather than as things each integration writes again.
If your estate has the first set of signals rather than the second, the lighter approach wins, and we would rather tell you that before the licence conversation than after it.
