If you are working in software development today, you probably know about the major shifts that microservice architectures have brought to our industry. Microservices (and the formerly popular Service Oriented Architectures) advocate splitting a software system into a series of small, encapsulated services, each with its own scope.
The advantage here is that backend engineers can work on small pieces of the application without interfering with unrelated code. Individual services can also be deployed one at a time, making maintenance and upgrades easier to manage.
The biggest challenges in implementing microservices are defining the scope of each service properly (in many microservices implementations you end up with a "micro-monolith") and integrating these isolated services when developers need data from multiple microservices at once.
A Federated Graph Interface is one option for gluing services together in a scalable, easy to manage way. A Federated Graph Interface is like an API gateway designed to help developers query many APIs at once using a single GraphQL interface.
In this article, you'll learn about federated data architectures and the way they can be used to query data from decoupled services. I'll focus on the Apollo Federation implementation that supports reading and mutating data stored across many instances. You'll see why this approach can help your team query and use data from microservices and query many GraphQL servers as if they were one.
The Benefits of a Federated Graph Interface
"UI developers love the simplicity of working with one conceptual API for a large domain. Back-end developers love the decoupling and resilience offered by the API layer...As we've grown the number of developers and increased our domain complexity, developing the API aggregation layer has become increasingly harder." - Tejas Shikhare, Senior Software Engineer at Netflix
Strong separation of concerns is the first benefit of implementing a Federated Graph Interface. This concept from computer science advocates the specialization of components in software, such that each part of the system is concerned with a single problem. By decomposing large components into smaller ones, each can be designed and managed by different teams using the best tech stack for each job.
The second advantage to Apollo Federation is its capability to reference entities from other subgraphs. This unifies your data catalog, allowing developers to compose new solutions with each subgraph defined in the integration layer. In this way, you can stack up several definitions from previously defined entities.
Finally, using a Federated Graph Interface allows you to extend previously defined entities. This mirrors the concept of inheritance in Object-Oriented Programming. In practice, it means that you can define a base "user" object and then extend it with data from other services in your microservice network.
Microservices and Data Federation
Two of the three principles of integrity in a GraphQL implementation declare that you should create a single graph per business even if that graph is implemented by different teams.
"When teams create their own individual graphs without coordinating their work, it is all but inevitable that their graphs will begin to overlap, adding the same data to the graph in incompatible ways. At best, this is costly to rework; at worst, it creates chaos." - Principled GraphQL
This fits nicely with the objectives of a microservices architectural style. Microservices dictate that you distribute the load of the system---or the effort to build the system---into small, independent teams working towards a common public interface at the end.
Using a Federated Graph Interface with microservices offers several advantages then:
Through a single graph, you can query more entities with a single query
Heterogeneous teams can share experience and skills
A common data catalog can be built by composing services through the federation
Some operational tasks can be centralized, for example, Access Control using tactics like Role-Based Schemas
This pattern also avoids the problem of centralizing the development of the entire graph on a single team or technology as this would essentially create a monolith and introduce all the problems that come along with them.
GraphQL vs. REST-based API Gateways
GraphQL and microservices are a great fit since they share common philosophical roots, but also because they complement each other. While there are other ways to manage a central access point for all your microservices, GraphQL federation offers some significant improvements over traditional, REST-based API gateways.
First, control of the response payload is given to the consumer. This makes the payload dynamic. REST APIs typically reveal the entire schema of a service without offering any control to consumers. GraphQL---and specially federated graphs---offer the possibility to query fragments of entities composed from others defined in different subgraphs.
Second, GraphQL allows for the definition of custom types. This type system lets you define each microservice with the types of data most appropriate to its business domain. In order to support the reference and extension features of a Federated Graph Interface, the types just need to become entities with key attributes.
Finally, GraphQL allows for incremental adoption of microservices. You can use your Federated Graph Interface to promote a slice of your monolithic service in an incremental way without losing any functionality on the backend.
This approach has allowed companies like Airbnb to split off each entity defined in a monolith into its own subgraph as they migrate pieces of their application to new microservices. This makes it possible to migrate from a monolith to fully distributed microservices in a progressive way---one service at a time.
The Components of a Federated Graph Interface
Once you decide that a Federated Graph Interface is right for your application, you have to come up with an adoption approach. The first step is to add an abstraction layer on top of one or more existing backend services.
Apollo Federation acts as the gateway and is connected to a series of GraphQL federated-capable services. The gateway acts as an umbrella to centralize access to each GraphQL implementation or microservice. These services can be maintained in-house, or they could be external applications.
For example, Strapi is a headless Content Management System (CMS) which provides a GraphQL API that is federation-ready with a simple change in its configuration:
{
"endpoint": "/graphql"
...
,"federation": false
}
You can use the headless capabilities of Strapi to implement common data models like e-commerce, blogs, or knowledge bases. These use cases are perfect for GraphQL federation as you may use the different subgraphs to offer the complimentary services that interact in a loosely coupled way.
How to Build a Federated Graph Interface
If you want to start testing this kind of approach to composing elegant and scalable solutions, you can reuse some of the Strapi starter repositories available for free. These include several common use cases for GraphQL backends accessible through a federation-ready interface compatible with Apollo. The starters also include examples of frontend frameworks you can use them with, like Gatsby, Next.js, or Vue.
For example, imagine that you wanted a graph in Strapi centered around a User entity. In addition to typical user fields (name, email, ID, etc.), you might want to link users with some external content like their posts on a blog.
If you designed this kind of relationship in a single GraphQL service, you might end with a something like the following:
type User {
id: ID!
, nickname: String!
, name: String
, posts: [Post]
}
type Post {
id: ID!
, title: String!
, tags: String
, text: String
, writer: User
}
As you see, both entities are coupled, but you can easily separate them by applying reference and extension of types. This will allow you to split the user into two separated subgraphs with independent implementations:
type User {
id: ID!
, nickname: String!
, name : String
}
-- External service definition
type Post {
id: ID!
, title: String!
, tags: String
, text: String
, writer: User
}
extended type User {
id: ID! @external
, posts: [Post]
}
In the above example, externalizing the Post entity and extending the User entity with a local type allows you to reference the original User type defined before. This kind of flexibility lets you create complex data models from microservices that define atomic entities that resolve single concerns.
If you need more examples or want actual code to get the idea about the implementation, this article is a good starting point. It shows you how to compose a User with Account and Reviews entities from different providers using a Federated Graph Interface.
Conclusion
Federated Graph Interfaces are a great fit for modern software architectures, and many large companies like Walmart and Netflix are taking advantage of them to help scale up their microservices.
Now, thanks to the availability of headless CMS like Strapi and ready-made gateways like Apollo, it's relatively easy for even small companies to implement a Federated Graph Interface quickly. The flexibility and scalability this setup provides will help you save time while building more flexible, scalable software.