Federation & Subgraph Architecture: The Domain-Owned Graph
Federation is not a GraphQL scaling trick — it is the application of domain-driven design to the schema layer. A subgraph boundary is wrong if it maps to a service, and correct only if it maps to a domain that a single team owns end-to-end.
GraphQL Backend & API Design
Federation & Subgraph Architecture: The Domain-Owned Graph
You own the schema and the resolvers — the schema is a public contract you can never silently break, and every resolver is a performance commitment you make on every query. When you introduce Apollo Federation, you add a third commitment: the subgraph boundary is a team boundary, and drawing it incorrectly creates the same coupling problems as a monolith, except now across a network. Federation earns its complexity only when each subgraph is owned end-to-end by one team — from the type User @key(fields: "id") declaration to the PostgreSQL schema behind it. When teams split subgraphs by technical layer instead of domain, Federation delivers all the operational cost of a distributed system with none of the organizational benefit.
This is Part 3 of the GraphQL Backend & API Design series. It builds on Part 1 (Schema Design: The SDL as a Domain Contract) and Part 2 (Resolvers, DataLoader: The N+1 Is Always Your Fault). Federation concepts require you to understand @key, which is GraphQL's distributed foreign key — Part 1's discussion of ID! as a semantic type is the prerequisite.
1. When Federation Earns Its Complexity
Federation is not a default architecture. It is a solution to a specific organizational problem: multiple teams needing to contribute to a single graph without coordinating on every schema change.
1.1 The Wrong Reason to Use Federation
1.2 The Right Boundary: Bounded Context → Subgraph
A correct subgraph boundary maps exactly to a domain that one team owns — including the database, the business logic, and the operational runbook:
A subgraph boundary is correct when a team can make any schema change within their subgraph — adding fields, changing non-null, deprecating — without opening a PR against another team's codebase. If your "domain boundary" requires a meeting with another team for every change, it is not a domain boundary.
2. @key — The Distributed Foreign Key
@key is the single most important directive in Federation. It declares which field(s) uniquely identify an entity across subgraphs, enabling the Router to resolve cross-subgraph entity references.
2.1 How Entity Resolution Works
When the Router receives a query that spans subgraphs, it decomposes it into a query plan:
The Router's query plan:
- Fetch
{ user(id: "u1") { name __typename id } }from Users subgraph - Use the returned
id: "u1"as the@keyto fetch{ _entities(representations: [{ __typename: "User", id: "u1" }]) { ... on User { orders { total status } } } }from Orders subgraph - Assemble and return the merged response
2.2 Federation 2.5 Key Directives Reference
| Directive | Purpose | Example |
|---|---|---|
@key(fields: "id") |
Declares entity identifier | type User @key(fields: "id") |
@external |
Marks field as defined in another subgraph | id: ID! @external |
@shareable |
Allows a non-entity type to be resolved by multiple subgraphs | type Money @shareable |
@override |
Migrates a field's resolver from one subgraph to another | orders: [...] @override(from: "legacy-orders") |
@interfaceObject |
Extends a federated interface across subgraphs (Fed 2.5) | type Node @interfaceObject @key(fields: "id") |
@requiresScopes |
Requires OAuth scopes to access a field | adminOrders: [...] @requiresScopes(scopes: [["admin"]]) |
3. Apollo Router vs. Apollo Gateway
Apollo Gateway (Node.js) entered maintenance mode. Apollo Router (Rust) is the current production gateway and handles the query plan execution for Federation 2.x.
Why Router over Gateway:
- Performance: Router handles query planning in Rust — significantly lower p99 latency under high concurrency than the Node.js Gateway
- Native SSE subscriptions (Router v1.40+): Built-in Server-Sent Events subscription transport without a separate subscription service
- Native OTel: Zero-config OpenTelemetry span emission per subgraph fetch, per field, per entity lookup
- Persisted Operations: Built-in manifest mode (v1.30+) — clients send operation IDs, not full operation documents
4. Schema Registry CI with rover
rover is the Apollo CLI for schema management. It is the CI gate between a schema change and a subgraph deployment.
4.1 The CI Pipeline
rover subgraph check runs in under 10 seconds (v0.20+). If your CI pipeline skips this gate "to save time," you have removed the only mechanism that catches breaking changes to client operations before they reach production. Make it a required check.
4.2 Composition Errors vs. Runtime Errors
Federation composition happens at the schema registry level — when you publish a subgraph schema, Apollo's composition validates that the supergraph remains valid. This catches errors at publish time, not at runtime:
5. Partial Data Semantics When a Subgraph Is Down
Federation changes the error handling contract significantly. When a subgraph is unavailable, the Router does not fail the entire request — it returns partial data.
This means the client MUST handle partial data in Federation-backed GraphQL:
Designing your Federation schema with aggressive non-null (orders: [Order!]! instead of orders: [Order!]) on fields resolved by separate subgraphs is dangerous. When the Orders subgraph is down, null propagates upward through the non-null chain and may null out the entire user field — even though the Users subgraph responded correctly. See Part 1's discussion of non-null semantics.

The Router decomposes one client query into parallel subgraph fetches, uses @key entity references to stitch results across domain boundaries, and returns a single assembled response — even when a subgraph is unavailable (partial data path shown in red).

Each subgraph is owned end-to-end by one team: their type definitions, their database, and their operational runbook. Cross-team references use @key entity lookups — not shared database tables or internal API calls.
Summary
| Concept | Rule |
|---|---|
| Subgraph = team domain | A subgraph boundary is correct only when one team owns it end-to-end: the SDL, the resolvers, the database schema, and the on-call runbook. |
@key is a distributed FK |
@key declares which field uniquely identifies an entity across subgraphs — the Router uses it to perform cross-subgraph entity resolution without requiring inter-subgraph API calls. |
| Router over Gateway | Apollo Gateway (Node.js) is in maintenance mode. Apollo Router (Rust) is the production gateway for Federation 2.x — native OTel, native SSE subscriptions, lower p99 latency. |
| Composition CI gate | rover subgraph check catches breaking schema changes before deployment — it runs in under 10 seconds and must be a required CI gate, not an optional step. |
| Partial data is the contract | When a subgraph is unavailable, the Router returns partial data — clients must handle null on individual fields independently, not just check the top-level data object. |
What's Next
Part 4 — Subscriptions on the Server extends the resolver contract to streaming: subscribe and resolve are two separate functions, and the PubSub broker decision (EventEmitter in development, Redis or Kafka in production) determines whether your subscriptions survive a rolling deployment or silently drop messages.
This article was developed with AI-assisted deep search, specification cross-referencing, and technical research synthesis.