Calls from Subgraph to Supergraph vs. Blackboard

Very early on in our adoption of Federation we put in place a hard line rule that no subgraph would ever make API requests into the Supergraph where it resides.

Once we started breaking out more and more logic from our monolith into subgraphs it became clear that we needed some way to access or manipulate data across multiple subgraphs from within a single query and/or mutation (I’ll focus on queries here). What we’ve done is make heavy use of the @requires directive to create queries that cascade down through subgraphs to perform both filtering and hydration, allowing us to effectively perform cross-subgraph/domain searches. This has a side-effect of having to always resolve at least one child property first in order to trigger the @requires directives but that hasn’t really been a big deal. We call this the blackboard pattern.

Let me give an example of this for clarity. Assume we have two subgraphs, one for Users which can be either cats or dogs and another for Events. Let’s say the Events Subgraph stores the host of each event but only their identifier (userId). Now I want to get all events hosted by dogs. Here’s how we would do that using this pattern:

query EventsHostedByDogs {
  eventsHostedByDogs {
    filteredEvents {
      host {
        name
      }
      date
      location
    }
  }
}

Here, the eventsHostedByDogs query would be resolved by the Event Subgraph returning, all events with their host’s userId regardless of whether they are a cat or dog. Why does it do this? Because the Event Subgraph doesn’t know whether the host is a cat or dog. That list of events comes in on an @inaccessible field. That field is required by the Users subgraph, which takes the data, looks up all the hosts and returns a list of events where the host is a dog on the filteredEvents field.

Make sense? Okay. So naturally the debate comes up here sometimes around alternatives to this pattern, as it can get quite complex and hard to understand when you start hitting 3 or more subgraphs. The primary alternative that is suggested is to call into the Supergraph from the subgraph to get the data it needs. Following that exact same example we would have the following query:

query EventsHostedByDogs {
  eventsHostedByDogs {
    host {
      name
    }
    date
    location
  }
}

Almost exactly the same, the difference is in the implementation. Here the Events subgraph would retrieve the list of events, then make an API call into the Supergraph to get information about the host via their userIds, then use that resulting data to filter out the events and return them to the consumer. Note that there is one significant difference between these to patterns in how the calls are structured. The blackboard pattern fires two reqests off to the subgraphs but neither of them waits for eachother. With the second method the Events Subgraph waits to return until the Users Subgraph returns.

I could write a small book on the pros and cons that have been brought up here for each but I think for the sake of discussion I don’t want to bias anyone here in giving their opinions. What we’re curious about is if people generally are following one of these two patterns: blackboard using requires versus calling into the Supergraph from a subgraph.

We are also generally curious if anyone has subgraphs calling into the Supergraph and what your policy on the matter is.

P.S. We have talked about other async solutions to this problem, such as replicating necessary data between subgraph data stores or creating caches of commonly accessed information but ultimately decided that the complexity wasn’t worth it.