Here's some food for thought: what do software architecture patterns and food trucks have in common?
To start, they both come in a variety of shapes, sizes, and flavor palettes. There are ones you love, a few you can't stand, and some you've never heard of. People hold strong opinions on the subject—experts and enthusiasts alike can argue for hours about which one is best. And ultimately, it's the little details that hold the whole thing together: they can make the difference between a good experience and a disaster.
For instance—will you walk away with exactly what you ordered? Do you have to do some extra work to get it right? And even if all the details are correct, what happens when your favorite service starts to become popular? When the queue is out the door?
The service is lagging, the clock is ticking, your stomach is growling. You find someplace else to get your snacks.
We expect the services we use to deliver a few key things. We want to receive what we've asked for—this means we get back something that's correct and complete. (No follow-up orders necessary!) We also expect speed—we shouldn't feel compelled to look elsewhere for the same kind of product.
So with these things in mind, how exactly does federated architecture make a difference? And how can we apply it to improve the process of building applications?
Federated architecture—also known simply as federation—is a pattern we can reach for when building our application. It pairs beautifully with GraphQL, because together they let us focus on those key pieces that matter to end-consumers. And it all happens without compromising our speed of development time.
Using GraphQL in the federated architecture pattern, different autonomous services make up the system. These services are like functions, or concerns, of the business. They each have their own individual focus, which frees up each service to work out exactly what kind of data and processes it cares about, and what piece it contributes to the whole.
Specifically, each service contributes its GraphQL schema. The schema is the full picture, or map, of all of the service's capabilities: the kind of data it works with, what it requires to fulfill requests, and what it will send back in its responses. A federated architecture comes together when these individual pieces, gathered across all of a system's various services, are composed together into a single GraphQL API.
This is where we get the "federated" in "federated architecture"—each service operates separately in its own domain, but it contributes to the whole and keeps the entire system running smoothly and efficiently. It also means the process of querying, or asking for data, from the federated graph looks and feels seamless:we can pick and choose bits of data that correspond to different domains of the system, but we don't have to worry about gathering up lots of different responses and piecing them together.
Let's go back to our food trucks to see how this works. (Now leaving the land of software-as-a-service (SaaS)—welcome to the realm of snacks-as-a-service!) 🍕
Lots of factors can contribute to a seamless snack-ordering experience, but let's inspect just a few. Most of us will start our journey with a look at the options.
Zooming out, we can identify some key areas: dishes, orders, and customers. Each of these domains has a focus worthy of dedicated time and resources, and it's through the synchronization of these pieces that customers can make transactions, we can fulfill and keep track of orders, and our system can function smoothly.
What would our "food truck" system look like in a single graph? As a customer, we can imagine writing a query for it that pings all the services we're interested in: a single BIG request that asks for everything at once. Something like:
And what about from the perspective of the organizers? The food truck manager; the event sponsors; the backend business people (who have LOTS questions about how sales are going and where improvements can be made)? They can piece a lot of different data points together, from every service, with a single big query:
Though packed together into one query, each component of our question tracks to a particular service. The huge benefit in a federated graph is that we don't have to consider which piece goes to what service when we ask for data: the single, central graph takes in each field we've asked for, looks it up in the schema, and sends it off to the service that's responsible for fulfilling it.
This means while our orders service is gathering data for order volume and spend, the dishes service is coordinating relevant menu information, while the customers service runs off to gather details about customers' order frequency. And you might have noticed that while orders, dishes, and customers are each managed within their own service, none of them is truly isolated from the others. To connect customer data with specific order data, for instance, we need a mechanism to correlate, link up, and package together related data even when it comes from different sources.
The power of a single graph means that all of our requests can go to one central point—a single gateway into our system—that can divide them up into pieces for each of the backend services to fulfill. In this federated architecture, each individual service has control over its own data and requirements. This means a lot more freedom for the team managing it— but also that it fits in as a piece in a larger system. And it's through the coordination of these services that we're able to make such robust requests with consistency and speed.
This single gateway also brings consistency to both frontend consumers and backend maintainers.
What happens when we make queries to the gateway? Well, whether we start your operation with a query, to read data, or a mutation, to change data, the shape of how we ask for types and fields from the backend remains the same. We don't have to worry on the consumer side whether we've used the right method, pinged the correct endpoint, or included a bunch of particulars to get a successful request. There's no extensive documentation to pore over, because the GraphQL schema clearly dictates everything we need—including variables or filters!—to write our queries correctly. This allows all of the clients or frontend applications using our GraphQL API to request and consume data consistently, and without surprises.
From the perspective of backend maintainers, there's a huge gain in flexibility: the decoupled nature of backend services means that we can swap them in and out as requirements change. New services can enter the graph, contributing new pieces of the schema, without impacting the other running services. A self-documenting schema means that there's no overhead documentation needed before users can build requests, and this same document communicates clearly whenever fields change, or are deprecated and replaced.
If we picture our system as a single monolith instead, we can see the loss in agility almost immediately. In a monolithic architecture, all of a system's focuses are built together in one codebase. This results in a lot of overlap between domains: we can't see as easily where orders stop and dishes begin, for example. Teams across all domains have to coordinate their features and deployments; they don't enjoy the same freedom of technology, or the ability to iterate rapidly. We lose a lot of speed as a result, because even small changes can have big impacts on the system that are difficult to calculate.
A monolith using REST can have dozens—sometimes hundreds—of different endpoints, all implemented with slight variations or specificity. These endpoints can prove brittle for downstream consumers, because it's difficult to know who's using what and when it's safe to make changes or ship a new version. Check out GraphQL for REST Developers to learn more about approaching GraphQL from a REST perspective.
How does this impact anyone who asks for data? To borrow from our food truck example, it means a lot more time to assemble the pieces. A single food truck that tries to do everything will quickly find that it doesn't excel at anything. (The drink orders are delayed by the sandwich assembly; and we can't start baking the muffins because the oven's cooking the tofu!)
In short, we need to make a lot more requests to a lot more endpoints, each fashioned to fit specific requirements. This latency can drag down the experience, not only in the volume of requests, but in the time it takes to piece all the information together. Answering some of our more detailed questions about the food truck business and sales requires much more work to gather, filter, and patch together the same answer that GraphQL and federation hand us on a swift silver platter.
The good news is that the elements of federation can be applied to even the fussiest of monolithic architectures. GraphQL and federation are both characterized by the principle of incremental adoption, which means that you can test them out in small doses. And even a little GraphQL can begin relieving even the most unwieldy and bloated system of some of its pressure.
Want to learn more? Check out this course on Odyssey to learn more about federating an existing monolith.
Separating our system by concerns and adopting federation principles incrementally are two of the underlying ideas that GraphQL enables. Both are essential to maintaining an agile system: we need the freedom to maintain separate business domains, and we definitely need the flexibility and speed that this division enables.
You don't have to tear down your system and rebuild it from scratch to adopt a federated architecture. There are lots of ways to start evolving toward a more flexible style of architecture that promotes speed, agility, and scaling throughout your system.
Where do you go from here?
GraphQL.com is maintained by the Apollo team. Our goal is to give developers and technical leaders the tools they need to understand and adopt GraphQL.