GraphQL Part 3: Federation and Service-oriented Architecture
In the previous two parts of this blog series on GraphQL (see part 1 and part 2), we focussed on its abilities in a monolithic or backend-for-frontend architecture; the benefits, drawbacks, and what questions a team should ask itself before introducing it.
Implementing GraphQL with such an architecture, while initially easy, runs counter to the trend of splitting services into multiple domains and microservices and runs into the same issues as other monolithic applications.
This blog post will therefore explain how GraphQL with a federated architecture addresses these pain points and what to look out for.
Part 4 of this blog series will showcase the practical implementation of Federation by continuing the case study of Mantel U.
Housekeeping: this blog refers to Federation v2 as “Federation”. Federation v1 as well as all other proposed solutions of dividing GraphQL schemas will be referred to as “schema-stitching”.
How does Apollo Federation work?
In a federated architecture, the GraphQL graph is split into multiple GraphQL subgraphs which are hosted on separate servers. These subgraphs are composed into a supergraph, which is exposed by a gateway through a single API endpoint. Incoming queries are routed by the gateway/graph router to the appropriate subgraphs and the returned data is composed and returned to the client in the requested format.
This approach keeps the advantages of a monolithic GraphQL schema, which allows clients to query all data from one endpoint, while also enabling teams to own independent parts of the graph and resolvers.
Apollo introduced Federation 2 with the goal to simplify the compilation of separately-managed graphs into one endpoint. The Federation 2 library ensures standardisation and makes subgraphs aware of each other’s object types and fields prior to graph compilation. While all subschemas must include custom types to support federation, the amount of code written is less extensive and more maintainable than with schema stitching.
A drawback of this approach is that it requires your team to learn and adopt Federation and moves you into the sales funnel for Apollo’s paid services. In this section we will be explaining Apollo Federation and evaluate its advantages and drawbacks before part 4 dives into our case study.
The following table compares GraphQL with a monolithic architecture vs. a federated architecture:
|Monolithic Architecture||Federated Architecture|
|Schema Design||Query-driven schema design because monolithic graphs are mostly designed for specific front-end needs.||
We recommend a schema-driven design approach, working from the bottom up:
|Object types||Object types contain a collection of fields which define the shape incoming queries can take. The entire repository can contribute towards fields in each object type.||Entities allow object types defined in one subgraph to resolve their fields across multiple other subgraphs. Each subgraph is only responsible for resolving the fields they contribute to the entity.|
|Introducing new features||The larger the monolithic graph gets, the more domain knowledge is required to implement features in alignment with existing standards & responsibilities.||Fields relating to new features initially need to be split into the correct domains. Afterwards, implementation is straightforward as standardisation is maintained through Federation’s declarative nature.|
|Frontend-facing API||Provides a single entry point through a GraphQL server||Provides a single entry point through the gateway|
|Fetching data||Resolver functions populate data for a single field in the schema. They fetch data from internal or third-party API endpoints or can directly connect to the data sources.||The gateway redirects the incoming request to the appropriate subgraph. Each subgraph functions like a monolithic graph, connecting to the data source they are responsible for through resolvers.|
Benefits and Drawbacks of a federated architecture
Federation successfully breaks the strong connection inherent in traditional GraphQL schema design, enhancing development speed and user experience. It achieves this by centering collaboration and communication around the supergraph/schema, simplifying interactions between various services while preserving independence between domains.
- Consumers continue being able to query a single API endpoint – no client-side changes required
- Different parts of the graph (= subgraphs) can be owned by separate teams
- Clear separation of concerns
- Subgraphs can scale independently
- Flexibility in how subgraphs are set up (security, caching, number of instances, etc) while maintaining compatibility
- Query speed may increase if subgraphs are set up to optimise parallel data-retrieval
- Move towards SoA possible as subgraphs start representing specific domains
- High dependence on Apollo framework and services
- High dependence on security and availability of gateway
- Unavailability of one subgraph affect subgraphs which share responsibilities of an entity with it
- Maintainable separation of concerns requires time and resources when Federation is set up
When can we use a federated architecture?
When GraphQL is first introduced:
GraphQL can be introduced with a federated architecture from day one. The main difference to the usual monolithic approach is that the first graph is already declared as a subgraph. The team can then keep adding to this one subgraph – just as they would have with a monolithic/bff architecture – or introduce further subgraphs.
We recommend this approach if you introduce GraphQL already with plans to have it be the only API endpoint for your domains (as opposed to wrapping REST endpoints) or if you want to employ a service-oriented architecture (SoA) from the get-go. With Federation, your team can keep separation of concerns front and centre throughout the development process.
If GraphQL is in use with a monolithi/cBFF pattern:
As discussed in part 1 and part 2 of this series, GraphQL is often introduced with a BFF or monolithic design. As the responsibilities of the graph increase, a tipping point is reached where the development effort and organisational pain of managing the graph outweigh the benefits.
In the past, developers had to either accept that pain or use forms of “schema stitching” (combining multiple GraphQL APIs into one unified proxy schema) to separate the graph into smaller domain-oriented entities. While schema stitching is still a widely used solution, it is declining in popularity as it requires a lot of discipline to ensure it is implemented and maintained correctly.
Moving towards a federated SoA
Stage 0: Monolithic Schema
Most organisations start out using a monolithic or BFF architecture for their GraphQL implementation. Ownership of the GraphQL server often lies with front-end clients, who have to collaborate with different backend domains to keep the graph up to date with changes upstream.
Stage 1: Adopting Federation (Hybrid SoA Federation)
When introducing Federation, we recommend taking it one step at a time. Organisations should first move the entire monolithic graph into a subgraph server and focus on setting up the supporting infrastructure. Once that has been achieved, the monolithic graph gets split into several subgraphs. To move towards a service-oriented architecture, these subgraphs should be split along the responsibilities of the domains they represent. Disturbance of BAU (business as usual) development activities are minimised by keeping ownership of each subgraph/entity with the team responsible for that domain.
Stage 2: Merging graph ownership with domain ownership
As the graph gets split into subgraphs, authority of each subgraph should be transferred to the domain the graph represents.
In the example below, Enrolment objects and logic has been split into the enrollment subgraph; the subgraph has been transferred into the Enrolment domain. It can now be developed alongside the upstream service, minimising friction and implementation lag. If desired, the existing REST endpoint in this example can be removed, so that the GraphQL server is the only API endpoint for front-end (FE) consumers. The graph itself now defines the contract between gateway and service; similar to the role Swagger/OpenAPI plays in traditional REST endpoint development.
Goal state: Full SoA Federation
Federation allows developers to bring their GraphQL graph in line with domain-driven development through a service-oriented architecture while continuing to offer FE consumers a single endpoint through which data-as-needed can be fetched.
The great selling point of Federation is thus keeping the positive attributes of “traditional” GraphQL architecture while – if implemented correctly – increasing development speed and removing friction in the intersection between BE services and the GraphQL API endpoints and resolvers.
However, this value is only realised if teams are allowed to invest time into implementing a thought-through splitting of subgraphs by domains and responsibilities, and if there is a long-term view of merging ownership of GraphQL APIs with their respective domain teams. Furthermore, as Apollo is by far the biggest solution provider for Federation, adopting it can move companies into Apollo’s sales funnel and lead to vendor lock-in.
Part 4 of this blog series will discuss with the help of a case study how teams might best introduce Apollo Federation to their project, what Apollo Studio can be used for and what questions an organisation should ask itself when moving their GraphQL implementation to a service-oriented architecture.