1

Background

In the scenario GraphQL Federation was designed for, you have numerous GraphQL microservices in the backend, each with their own Schema Definition Language (SDL).

Theoretically, these services are able to start and run independently and can begin serving GraphQL requests right away. In order to tie these services together, you also must have a GraphQL gateway service that obtains knowledge of the disparate SDLs (via service discovery or some other mechanism) that is able to reconcile these SDLs and present a unified API for consumers. This is nice, since the consumers never have to know about the underlying services providing the data --all they see is one unified graph.

Problem

The problem here is that GraphQL Federation essentially requires that the backend services have knowledge of one another and the kinds of entities that exist in the graph. In Apollo Federation, every entity in the graph must belong to a canonical 'owner' service. For instance, anything related to a User entity would be provided by a User service. If you've modelled your graph such that a User can have many Products, and the Products portion of the graph is provided by a Products service, then you'd use special directives (like @external) to indicate to the gateway that an external service provides that data.

Example

To illustrate, here is a User GraphQL schema that references an external entity, Product:

type Query {
  user: User
}

type User {
  id: ID!
  name: String
  products: [Product]  # Products is defined in the `Products` service.
}

type Product {
  id: ID! @external
}

Then in the Products service, you'd simply define a Product:

# The `Products` GraphQL service
type Query {
  Product: Product
}

type Product @key(fields: "id") {
  id: ID!
  name: String
  upc: String
}

Question

My question is: Isn't this an architectural smell?

The User service is now tightly coupled to the Products service. The gateway must also be aware of implementation details of the other services, since it needs to understand Apollo Federation's custom annotations (@extend, @provides, @external), which means the gateway is now also tightly coupled to service implementations. If I wanted to change the Product entity name into something else, for example PurchasedProducts, I would have to touch the Products service, the User Service (as well as any of the other possibly dozens of services referencing Product).

I've seen that many big orgs are now using federation in one form or another, many of them choosing Apollo's specific implementation --how are they dealing with this coupling issue? I thought the whole point of microservices was to decompose a monolith into small, independent pieces of consumer-agnostic logic that are easy to deploy and scale. But what Apollo Federation seems to encourage is a sort of distributed monolith architecture that requires a high amount of coordination in order to succeed. Isn't that an anti-pattern?

1 Answer 1

2

Couple of things.

First, decomposing the monolith isn't just about having microservices that exist in a vaccuum.

Many services before this (BFF-style) were hand-rolling their orchestration. Instead, federation allows us to not hand-code BFFs from scratch by offloading the orchestration logic into a statically validatable schema.

Now, we can have a plethora of backing services, each with their own test and deploy strategy. This means they can also have their own SLOs around latency, availability, etc. In a monolithic world, you have to be all things to all people.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.