Why you should try Apollo Federation

Serhii Ohorodnyk
Zenjob Technology Blog
7 min readNov 22, 2022

With the rising adoption of microservices, software engineering teams are facing challenges in how to implement the communication layer between backend and frontend components. This is especially noticeable when migrating from an existing backend monolith: With the data scattered across multiple services, how can we maintain a good developer experience when creating frontend-facing APIs?

Challenges

Let’s take a closer look at some of the common challenges teams face. Later on, we’ll talk about what Apollo Federation is and see how it can make it easier to address those challenges.

Aggregated views

At Zenjob, we’re in the process of splitting our monolith into microservices, and one of the challenges we see is in the job feed: Our frontend application fetches a list of jobs from the backend. Every job contains its company information, such as the name, address, etc. In a monolith, there’s a single API for the frontend to get the list of jobs, and the data is resolved from two database tables: “jobs” and “companies.”

But if we decide to split the monolith into microservices with “jobs” and “companies” as separate services, how and where does the data aggregation happen in order to fulfill the frontend requirements?

Amount of frontend APIs

Another challenge is coming up with having multiple frontend applications in a single platform.

Here at Zenjob, we have three different types of frontend applications, each of which serves a different purpose: B2C for people looking for a job, B2B for companies requesting staff for their jobs, and an admin panel for our internal operators to support B2C/B2B flows.

All three application types operate with the same data, but each in a different context and with different requirements.

For example, you might need a search jobs API in all three applications, but each of them would need different fields from the job type: The B2C app is mobile and doesn’t want to overfetch data to save network bandwidth; the B2B app needs to get information about the person who will perform the job, but shouldn’t have access to sensitive personal information; and the admin panel needs everything.

Microservice patterns to the rescue

This next section will provide an overview of some microservice patterns that can help tackle these challenges. If you’ve worked with microservices before in a RESTful setup, you might already be familiar with some of these patterns.

CQRS pattern

One way of solving these challenges and keeping services loosely coupled is to use the CQRS pattern. With CQRS, in addition to the “jobs” and “companies” services, we’d create another service for searching jobs. This service would gather all the necessary information through events from both services, save it in a read-only replica, and expose it via a separate API for each frontend component. In turn, we could build separate APIs for each frontend component.

For each aggregated view, we’d create a separate replica component in the future. This would allow us to keep the separation of concerns between services while fulfilling the demands of each frontend application.

BFF pattern

Another pattern that can help us is a Backend for Frontend (BFF) pattern. In this scenario, we’d create a separate BFF for each of the frontend apps. BFFs would be responsible for fetching data from both services and aggregating it into a single interface required by a specific frontend application. In this way, separation of concerns is respected on the service level, and each application has its own BFF that can fetch and aggregate data from any additional backend service in the future.

Drawbacks

While both of the patterns above can help us overcome the challenges, they introduce maintenance overhead: With each solution, we need to create and maintain several more services to fulfill the requirements.

At first, it’d be just time consuming, which is a reasonable tradeoff to get scalable architecture. But with time, some additional “inconveniences” could appear, such as:

  • For some entities (like “job” mentioned above) it makes sense to create different APIs for different frontend applications, but some other entities will have the same requirements in all frontend applications. But with the BFF approach, you’ll find yourself implementing the same logic multiple times and copy-pasting the code.
  • Frameworks, libraries, and other cross-cutting concerns have to be updated multiple times in all replica services or BFFs.
  • BFFs or replica services may become “fat” with business logic that doesn’t belong to them, but rather to backend services, and you’d need to clean it up.

Enter Apollo Federation

There’s another solution that can help us, and it doesn’t have the above-mentioned drawbacks. Enter Apollo Federation. This is an architecture approach based on GraphQL, and it assumes you have basic knowledge about what a GraphQL resolver is and how to think in graphs. If you’re not familiar with these concepts, please refer to the official GraphQL documentation.

In Apollo Federation, each microservice that’s meant to have a frontend-facing API exposes its interface via a federation-compatible GraphQL schema, thus forming a subgraph. These subgraphs are then unified into a supergraph by one specialized service, Apollo Router, which becomes the entry point for frontend applications.

If you want to know more about supergraphs, subgraphs, and Apollo Federation in general, I suggest checking out the official documentation (which is really good!) or even completing the Apollo Odyssey course.

How Apollo Federation can help

Just like BFF, Apollo Federation is an implementation of the API composition pattern. But it stands out in two major ways:

  • Apollo Router can resolve data solely based on the subgraph schemas. There’s no need to write code to map types from backend services to frontend applications or create extra endpoints, proxies, etc. In other words, there’s no code to maintain in the Apollo Router! It’s even distributed as a Docker image you can run in your infrastructure.
  • Subgraphs are split not by types, but by concerns. Each subgraph has its own types it adds to the supergraph schema, but it can also extend external types that are owned by other services. For example, a `paymentDetails` field on a `User` type can be a payment service concern, and not a user service concern (even though it owns the `User` type). So it makes more sense to extend the `User` type in the payment subgraph and keep the user service clean.

For our use case with the job search API above, this is important because we can get all the benefits but avoid the hassle of maintaining extra services. In a nutshell, a potential jobs query may look like this:

Big kudos to the GraphQL tools team for creating a type merging diagram. This diagram is based on their work, but it’s tailored to Apollo Federation.

In this setup, we have separation of concerns in our backend services, so each service only has knowledge about things it needs to know. All frontend applications use the same “jobs” query, fetching only fields they need to avoid overfetching. Because Apollo Federation is based on GraphQL, each service can create separate resolvers for each field with dedicated authorization checks, in turn protecting sensitive fields. And since Apollo Router automatically composes the supergraph from subgraphs, there’s no extra service to maintain, and it can’t get “fat” with unnecessary business logic.

Concerns and potential problems

If reading this got you excited about using Apollo Federation already, awesome! As with every approach though, it also has some drawbacks.

Schema governance

Now that you have a supergraph that represents all your subgraphs, it’ll be more and more difficult to keep the supergraph schema clean as your organization grows and you have more and more teams contributing to the joined schema. If the teams don’t communicate well, you might have conflicts in the schema or lack of a connection between domain types where you’d expect there to be one.

To mitigate this issue, it’s important to develop a strategy early on detailing how to govern the schema so that it represents a truly unified graph. For example, you might want to create a “Graph Champion” role on your teams to make sure changes to the supergraph schema are aligned on a strategic level.

If you’d like to know more about scaling best practices with Apollo Federation, check out the Enterprise Guide from Apollo Federation.

Learning curve

If you’re new to GraphQL, it can be a big challenge to grasp everything so as to get the best from Apollo Federation. There are simply too many new concepts that require a certain mindset shift. If it’s done wrong, it can be more harmful than helpful. My suggestion would be to start slow, master GraphQL alone first, and only then turn your attention to Apollo Federation.

Conclusion

Apollo Federation isn’t a “silver bullet.” It’s not a replacement for event-driven architecture by any means, and in fact, you may still need both simultaneously. Rather, the aim of Apollo Federation is to reduce complexity and the amount of code created and maintained to fulfill demanding frontend applications. As with any other solution, you need to figure out whether its benefits outweigh the drawbacks for your scenario, but hopefully this post helps you make a more informed decision.

--

--