Apollo Docs
/

Separation of concerns

Organizing schemas by features and teams


Apollo Federation introduces an important new principle to modular schema design: proper separation by concern. It's the key architectural quality that allows separate teams to collaborate on a single product-centric graph without stepping on each other's toes.

When you start thinking about how to break up a monolithic schema into modular parts, it can be tempting to divide things up by type. For example, you could make one module responsible for the User type, another one for your Product type, and a third one for the Review type:

3 separate types divided into 3 services

This may seem to make sense at first, but it quickly breaks down. The problem is that features or concerns usually span types. For example, suppose we want to add a new topNegativeReviews field to the Product type in the above schema. Even though it lives on the Product type, this addition is almost certainly the responsibility of the team responsible for reviews. This isn't just a conceptual point about team structure: the product service won't know how to resolve a query for topNegativeReviews, because data about reviews is stored in the reviews system (as a reviews table with foreign key references to products, for example).

Another way to think about the same problem is to consider an important type like User or Product. Often, no single team controls every aspect of such types, and it becomes quite awkward to colocate the definitions of each field on such types.

So instead, Apollo Federation allows you to extend an existing type with additional fields, using GraphQL's extend type functionality. That means we can break up a schema across boundaries that correspond to features or team structure. For example, a team working on a new reviews feature could develop their changes in one place, combining new types like Review with extensions to existing types, like adding the ability to navigate from a User or Product to the associated reviews:

Splitting those 3 types by data source rather than by type. Includes type extensions across services

The result is the best of both worlds: an implementation that keeps all the code for a given feature in a single service and separated from unrelated concerns, and a product-centric schema with rich types that reflects the natural way an application developer would want to consume the graph.