A single GraphQL monolith serving 70 clients⟶Microservices routed through a Smart Orchestrator
Agoda's GraphQL API had backed every client app since 2016. To split it into microservices without breaking any of the 70 clients depending on it, they migrated the clients before they touched the backend.
Move the clients first, the servers second — a routing table stands in for the rewrite until the rewrite is ready.
One GraphQL service, seventy clients, eight years of accretion
Since 2016, Agoda has run a single monolithic GraphQL API as the backend for its Hotel Management application and everything built on top of it — eventually 70 client applications, all reading and writing through the same schema. As new business domains arrived, their backend logic landed in the same codebase, because that’s where everything else already lived.
It worked, for a while. But a service that backs 70 clients accumulates more than features — it accumulates coupling.
REQUIREMENTS
Whatever replaces it has to be invisible to 70 client teams
The target was six logical microservices, split along business-capability and data-ownership lines. But the real constraint wasn’t the split — it was who had to feel it. Agoda needed a way to move traffic domain by domain without asking any of the 70 client teams to change a line of code, because a migration that requires 70 coordinated client releases is really 70 migrations, not one.
OPTIONS
Apollo Federation assumes the microservices already exist
The obvious first candidate was Apollo Federation, the standard way to compose a GraphQL schema out of multiple services. Agoda evaluated it and ruled it out: Federation “works best with existing microservices,” not with the gradual decomposition of a monolith that’s still mostly whole. Adopting it would have meant standing up all the target services before having any safe way to cut traffic over to them — backwards from the incremental, low-risk migration Agoda actually needed.
DECISION
Smart Orchestrator: a routing table with no business logic
Instead, Agoda built the Smart Orchestrator — a custom client library whose only job is routing. It queries each domain’s schema through introspection, folds the results into an in-memory Microservice Mapping Table, and uses that table to send each incoming request to whichever service currently owns it — monolith or microservice, transparently. Because it holds no business logic of its own, it can be pointed at a new backend the moment that backend is ready, without a redeploy on the client side.
RESULTS
Three of seven, and zero client-side code changes
To find the real domain boundaries, Agoda ran ETL jobs over production GraphQL traffic from all 70 clients and classified every query as Simple, Moderate, or Complex based on how many domains it stitched together — because a query touching three domains can’t move until all three are ready. That analysis is what surfaced the roughly 100 repositories carrying stitched queries in the first place.
By the time of writing, three of the seven planned microservices had been transitioned this way. Agoda’s own framing: “we had to do zero code change on the client side to move traffic extracted to the microservice.”
A migration that starts with the clients instead of the servers buys you invisible cutovers — the backend changes underneath, and none of the 70 apps calling it ever have to know when.
Other teams also decoupling clients from a GraphQL backend before the backend itself was ready to change.
A plain-language, AI-drafted and human-edited retelling of the article published on medium.com, reorganized and explained in our own structure and words, with original analysis in the editor's note above. The facts, numbers, and decisions belong to the original author and are not altered. For the full depth, read the source.