How to go about federated entities

Hey!

We are pretty new to the federated graph approach and hope to have some feedback if we are trying to complicate too much by splitting it out into smaller entities.

Examples:

We have a Ticket:

type Ticket @key(fields: "id") {
    id: ID!
    ownerId: ID!
    categoryId: ID!
    title: String!
    data: String
    startDate: DateTime!
    endDate: DateTime!
    venue: String
    county: String
    section: String
    seat: String
}

extend type Category @key(fields: "id") {
    id: ID! @external
    tickets: [Ticket!]!
}

And we have a Category

type Category @key(fields: "id") {
    id: ID!
    name: String!
    type: CategoryType!
}

extend type Ticket @key(fields: "id") {
    id: ID! @external
    categoryId: ID! @external
    category: Category @requires(fields: "categoryId")
}

We now need to implement a way to list Products for sale, and set listPrice etc. And in the future even bids. So my idea was to create a new federated service that handles listedProducts like below:

type ListedProduct @key(fields: "id") {
    id: ID!
    sellingPrice: Float!'
    ticketForSale: Boolean!
}

extend type Product @key(fields: "id") {
    id: ID! @external
    ticketForSale: Boolean!
    sellingPrice: Float!
}

So far, so good. But where I run into troubles is where I would want to render only listedProducts under the Category entity. This would need a 3 way reference in order to resolve, how would I do this without communicating directly between the services?

Expected outcome:

query {
  category() {
    // Only listedProducts should render here, but with the type of a Product.
    products() {
     id
     ....
    }
  }
}

Are we complicating it too much? Should we just handle the isForSale logic in the existing product schema?

It doesn’t look like a ListedProduct is a Product. Maybe use an interface and specify that ListedProduct is what’s returned there, but personally I would probably use category.listings if they’re really different things.

I would just try to represent things “as they are” and try not to surface up the concept of join tables and half-in-half-out entities.

You have a product, a product has listings. A listing is probably in one or more categories. A listing could live with the product service, categories service, or on its own. I would probably make it on its own so that you can do things like microservice autoscaling in fine detail, that way if the product service is really computationally expensive it doesn’t also spin up more services that handle Listings, as well.

When you query a category, you just go in the opposite direction of the above to get to the same data.

query {
  category ( # Category
    id
    listings { # [Listing] or ListingConnection
      id
      items/products { # [Product] or ProductConnection
        id
      }
    }
  }
}

As for whether or not the product is for sale and which service should own it, that’s a question of your sale rules, to me. If a product can be sold in one category and not another, but still listed for some reason, I guess the Listing service should handle it. Otherwise, the Product service should probably handle it, as the product service should have all of the information needed to determine that, and it probably should own that information too, now and going forward.