Is "Context Entity" Pattern a Best Practice for Multi-Service Relationships in Federation?

Hi Apollo community!

I’m working on a federated supergraph (30 subgraphs) - it’s gotten pretty messy and is not working well. It’s more like a collection of unrelated rest endpoints. There are two problems with the graph I’m trying to solve…

  1. The standard for a subgraph resolver to get data from another service is through the supergraph gateway. I’d rather rely heavily on schema design + federation directives where it makes sense.
  2. The schema is not designed in a demand-oriented way wherein clients can easily query for the data they need. (lots of water-falling request chains)

So I’m trying to start to untangle this and make a proof of concept of a high-leverage place where we implement a pattern which can be used as a blueprint moving forward. A “context entity” pattern for handling relationships between entities across multiple subgraphs. I’d love to get feedback on whether this is a recommended approach.

The Pattern

I have a UserCatalogItemContext entity that represents the relationship between a User and a CatalogItem. Multiple services extend this entity to contribute their domain-specific data. In this specific relationship, there are at least 3 subgraphs who want to contribute to the entity in the context of a specific User/CatalogItem combo. (For example - transcripts, rewards, assignments, reviews, etc.) This structure allows almost a “resolution hub” where these subgraphs can grab any field from both user entity and CatalogItem entity via @requires to resolve their fields they are contributing…

# Users Subgraph
type UserCatalogItemContext @key(fields: "userId catalogItemId") {
  userId: ID!
  catalogItemId: ID!
  user: User!
}

# Catalog Subgraph
extend type UserCatalogItemContext @key(fields: "userId catalogItemId") {
  userId: ID! @external
  catalogItemId: ID! @external
  catalogItem: CatalogItem!
}

# License Subgraph
extend type UserCatalogItemContext @key(fields: "userId catalogItemId") {
  userId: ID! @external
  catalogItemId: ID! @external
  user: User! @external
  
  canAccess: Boolean! @requires(fields: "user { organizations { name } } catalogItem { tier }")
}

# Transcripts Subgraph
extend type UserCatalogItemContext @key(fields: "userId catalogItemId") {
  userId: ID! @external
  catalogItemId: ID! @external
  
  transcript: DigitalTranscript @requires(fields: "blablabla")
}

Query Example

This enables queries like:

query {
  currentUser {
    catalogItem(catalogItemId: "cat-001") {
      user { name }              # from users
      catalogItem { name price } # from catalog  
      canAccess                  # from license (using @requires)
      transcripts { status progress }     # from transcripts
    }
  }
}

Note that in the current schema, the frontend has to make queries like the below after calling currentUser and querying for into about the CatalogItem….

getPdpTranscriptByUserIdAndCatalogItemId,
reviewByUserIdAndReferent,
getTrainingAssignmentByUserIdAndCatalogRootItemId,
rewardsByUserIdAndCatalogRootItemId

Question: Is this a commonly used pattern in federation architectures? Or is there another way?

Thanks for any insights!