Skip to content

Refactor merge script to streamline @boundary directives #134

@gmac

Description

@gmac

Bramble's present implementation of the @boundary directive has several indirections, and could be greatly streamlined. Myself and @exterm are considering some uses and would be interested in pursing some refactors of the merge script. While there's discussion of supporting config alternatives to directives, we believe that a refined SDL could work in tandem with this config objective, and improve the overall implementation of the tool in a backwards-compatible manner.

Current SDL limitations

  • The @boundary directive is required on both types and queries. This mapping is complex and allows devs to create config errors for themselves. It should only be necessary on boundary queries, and the types inferred from the queries.
  • Also, @boundary configuration is required on types and queries in all services. This is awkward when a service has an ID-only boundary type (type Gizmo { id:ID! }) that will never require an inbound request to fetch it, yet must provide a query for the boundary contract.
  • All queries with a @boundary directive are hidden from the gateway; they should be allowed to remain public (Allow public boundaries #84).
  • Boundary queries should allow an abstract interface to be used (Support for Node interface-based federation. #96).

Proposed SDL refactors

We propose remedying these problems by refactoring the @boundary directive and merge script to use the following signature:

directive @boundary(public: Boolean=false, types: [String!]) on FIELD_DEFINITION

With this new signature, the following happens:

  1. The @boundary directive no longer appears on types (we'll just ignore it on types, thus being backwards compatible)
  2. Boundary types are inferred as all types that provide a @boundary query somewhere in the graph, with some rules:
  • A @boundary query that yields a type appearing in only one service isn't actually a boundary; we'll allow the directive though because you may be incrementally rolling out service schemas with this new boundary.
  • A type that contains unique fields beyond just id without a @boundary query is an error (a boundary query is required to access unique data).
  • This pattern allows id-only types to be considered boundaries without needing to provide a useless query.
  1. Setting @boundary(public: true) will include the query in the gateway schema, and omit the boundary directive.
  2. Setting @boundary(types: ["Gizmo", "Gadget"]) will limit the scope of boundary types provided by a query, allowing for boundary queries to (eventually) return abstract types. This is mainly thinking around the scenario where a type may be available through multiple queries in the service, and we want to direct the gateway to a specific query for the type. This is kind of an edge case, and probably doesn't need to be a first-round feature.

All told these rules are backwards compatible, simplify the SDL, and lend themselves to being represented in a configuration-only manner.

Namespace

The downside here is that this does create inconsistencies with the @namespace directive, which I’d probably leave untouched for now. In general, namespace seems finicky to me because it makes assumptions about type root-ness, which isn’t guaranteed in the GraphQL type system (the object type assigned as the root query may again be returned from anywhere in the graph, thus root access is circumstantial).


Curious on @nmaquet and the Movio team’s take on this refactor before pursuing it. This has been gnawing me for a while, and now we have multiple teams prototyping with Bramble. Would be nice to get boundaries spruced up for them.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions