01 Migration architecture Ferry & cruise 2025 — 2026
Two hundred integrations, counted before designed
A European ferry and cruise operator was leaving its data centre, and two hundred or so integrations had to leave with it: Java services, Apache Camel routes, a message broker, and a long tail of file transfers and database links. I picked the engagement up at the handover from sales, in discovery, and led the integration architecture through to an approved design. Nothing here is built. The build starts after my tenure, which is the most important sentence on this page.
- integrations inventoried
- 200+
- assigned a target pattern
- 50+
- low-level designs written
- 20+
01
Context
The driver was not cost and it was not fragility, though both were real. It was a date: the client had committed to leaving its data centre, and everything running there had to have somewhere else to be. That makes migration architecture a different discipline from greenfield integration. You are not choosing the best target for each integration. You are choosing a small number of targets that two hundred integrations can be moved to in waves, by people who did not write them, before a date that will not move.
Nobody could tell me how many integrations there were. There was a number everyone used and no list behind it. The estate had grown over a decade or more: Camel routes doing transformation and routing, Java services with integration logic inside them, a message broker carrying the asynchronous traffic, and file and FTP transfers and database links underneath all of it doing the work nobody had got round to replacing. Some of it was documented. Most of what was documented was out of date.
My seat was the integration lead doing the architecture work, not a titled architect. I set up the analysis structure the discovery ran on, helped select the team for it, and ran weekly workshops with the client's application architect, walking one domain at a time. Senior Azure architects on the engagement reviewed what I produced and approved it as the baseline the build starts from. That approval is what this page can claim. A production record is what it cannot.
02
Constraints
- Two hundred of them
- Too many to design individually, and too varied to force through one pattern. The number itself had to be established rather than repeated.
- A date on the building
- A data-centre exit sets the schedule. An integration that has not moved by then has nowhere to run, which removes "leave it for now" as an option for anything.
- Documents, not code
- Discovery ran on walkthroughs and whatever documentation existed. I was classifying integrations I could not run, and the classification is what the estimate rests on.
- No big-bang cutover
- The estate has to move in waves, which means the legacy broker and its replacement are both live for months, and nothing may be processed twice while they are.
- Someone else's network
- Connectivity back to the data centre already existed, inside a platform the client runs. Anything I designed had to live behind it rather than beside it.
03
Architecture
Scroll the diagram sideways
- 1 Camel routes become Logic Apps Standard workflows, with Functions for the steps where the logic is code rather than orchestration. The largest of the four groups, and the one the pattern catalogue earns its keep on.
- 2 Broker traffic moves to Service Bus. A bridge runs between the two while both estates are live, because the waves have to be able to move independently of each other.
- 3 API Management as a façade over the Java services that are not being rewritten yet, so a consumer stops depending on where its backend runs before the backend moves.
- 4 File, FTP and database-link transfers become managed storage with Data Factory or Logic Apps around them — the long tail, and the part most likely to be forgotten until a cutover.
- 5 The client's existing shared hub. Putting the integration spoke behind it reuses a reviewed path back to the data centre and gives up the team's own control of the network.
- 6 The on-premises data gateway for the legacy databases. It is in the design and was never built, and it is the one component that keeps a dependency on the data centre being left.
04
Decisions
What was considered, what was chosen, and what that choice cost. The last line is the one that matters.
Decision 01
Count the estate before designing any of it
- Considered
-
- start with the integrations the client already called painful
- sample a representative slice and extrapolate the rest
- a full inventory with a complexity classification, before any target design
- Chose
- A complete interface inventory with a complexity classification against each entry, built domain by domain out of the workshops, before committing to a single target design.
- Because
- Two hundred is an estimate until someone writes the list, and every number that matters downstream is derived from it: the wave plan, the team shape, the estimate, the exit date itself. Starting with the painful integrations optimises for the client's memory rather than the estate, and the painful ones are rarely the ones that block a cutover. Sampling would have produced a defensible average and no wave plan, because you cannot schedule an average. The classification is what turned a list into a plan: it is the difference between "two hundred integrations" and "forty that are a week each, a hundred and thirty that are a pattern application, and thirty that need someone to think".
- Gave up
- Weeks spent producing no design. On an engagement with a date, that is the hardest cost to defend, and it has to be defended before the count exists rather than after.
Decision 02
Four target patterns, not two hundred designs
- Considered
-
- design each integration on its own merits
- one universal target pattern everything is forced into
- a small catalogue of patterns, with every integration assigned to one
- Chose
- Four patterns, each with a worked reference design: Camel routes become Logic Apps Standard workflows with Functions for the code-heavy steps; broker traffic moves to Service Bus; the Java services that are staying get an API Management façade; file, FTP and database-link transfers become managed storage with Data Factory or Logic Apps around them. Every integration in the inventory carries the pattern it is assigned to.
- Because
- A catalogue is what makes a migration estimable and delegable. Once an integration has a pattern, its estimate comes from the pattern rather than from a conversation, and a developer who has done one has largely done the next. Designing each one individually does not scale past the people who can design; forcing everything through one pattern produces a design that is wrong for a third of the estate and discovers this during the build, which is the most expensive moment to discover it.
- Gave up
- Four patterns will not fit two hundred integrations exactly. A residue needs its own design, and the catalogue has to be allowed to say "not this" rather than absorbing everything — a pattern that fits everything has stopped carrying information.
Decision 03
The Java services keep running, behind a façade
- Considered
-
- rewrite the estate onto Azure-native services in one programme
- re-host the Java services on containers and move on
- a façade over what stays, and a bridge so waves can move independently
- Chose
- API Management in front of the Java services that are not being rewritten yet, so consumers move to the new boundary before their backends do, and a bridge between the legacy broker and Service Bus so a wave can cut over without waiting for the estate around it.
- Because
- The exit date buys the migration and nothing more; it does not buy a rewrite of a decade of integration logic, and a plan that needs one will miss the date and be blamed for the rewrite as well. Putting the façade in first means the consumer contract stops depending on where the backend runs, which is the property that lets the waves be scheduled by risk rather than by dependency. The bridge is the same idea applied to the asynchronous traffic.
- Gave up
- Two estates live at once for months, with a bridge that somebody owns and a duplicate-processing risk that has to be designed against rather than watched for. It also lets the last wave slip indefinitely, because a façade makes the legacy comfortable — that needs a governance answer, not an architectural one.
Decision 04
One hub, not an integration estate of its own
- Considered
-
- a standalone integration subscription with its own egress to the data centre
- public endpoints with IP restrictions and no private path at all
- an integration spoke peered into the hub the client already runs
- Chose
- A hub-and-spoke landing zone with the integration workloads in a spoke behind the client's existing shared hub and firewall, private endpoints for the platform services, and the on-premises data gateway in the design for the legacy databases that cannot be reached any other way.
- Because
- The connectivity back to the data centre already existed, terminated in that hub, and it had been through the client's security review once. Building a second path would have meant a second review, a second set of firewall rules and a second thing to keep in step for the whole coexistence period — all to avoid a dependency the migration does not actually want to avoid. This was the decision the senior architects pressed hardest on, and the pressure was right: it is the one that is most expensive to reverse.
- Gave up
- Per-team autonomy, permanently. Every firewall rule, every new private endpoint and every address-space change becomes a request against a platform team the integration team does not sit in, and the migration's throughput now has a dependency with its own queue.
05
Trade-offs accepted
- This is a design with an approval, not a design with a production record. It was reviewed by the engagement's senior Azure architects and signed off as the baseline for the build, and the build begins after my time on it ended. Everything on this page should be read as what was decided and why, not as what survived.
- The complexity classification is the load-bearing assumption in the whole plan, and it was made from documentation and walkthroughs rather than from running the code. Where it is wrong, the wave plan and the estimate are wrong with it, and the error will not appear until a build team meets the integration.
- The gateway for the legacy databases is in the design and was never built. I would expect it to be the first thing the build phase argues about, because it is the one component that keeps a dependency on the data centre the client is leaving.
- Assigning patterns from an inventory produces an estate that is consistent and slightly wrong everywhere, rather than correct where somebody thought hard and unexamined elsewhere. For a migration with a date, consistent and slightly wrong is the better failure mode. For a platform that has to last a decade afterwards, it is a debt.
06
Stack
- Logic Apps Standard
- API Management
- Service Bus
- Azure Functions
- Data Factory
- Hub-and-spoke landing zone
- Private Endpoints
- Bicep