В основном это вопрос о том, как моделировать алгебраические типы данных в GraphQL. В частности, как смоделировать совместный продукт, в котором некоторые возможности являются одиночками, а другие являются продуктами. Для тех, кто не знаком с этой терминологией:
продукт – структура данных, объединяющая данные, которые должны отображаться вместе (см. также кортеж, запись, класс данных). , так далее.).
совместный продукт – структура данных, представляющая взаимоисключающие варианты (см. также объединение, либо, тип суммы и т. д. )
singleton — тип, который имеет только один член. Другими словами, он не имеет свободных параметров и не содержит никаких данных, кроме собственного присутствия.
алгебраический тип данных – набор типов произведений и сопутствующих произведений, которые ссылаются друг на друга (возможно, рекурсивно), а также на синглтоны. Это позволяет моделировать произвольно сложные структуры данных.
Мой вывод на данный момент заключается в том, что для этого нет абсолютно чистого и общего ответа. Вот несколько подходов, каждый из которых имеет компромиссы:
Использовать null
(ограничено)
Если в вашей структуре данных есть только один необязательный синглтон, вы можете использовать нулевое значение для представления синглтона. См. [Herku's answer][1]
для списков. Я включил это для полноты, но оно не распространяется на структуры данных с несколькими синглтонами. И в тех случаях, когда одноэлементный вариант не обязательно представляет отсутствие или пустоту, это может быть неудобно.
Пользовательские скаляры
Если вы хотите отказаться от необходимости проверять внутренние данные в вариантах, у которых есть свойства, вы можете сделать весь вариант непрозрачным пользовательским скаляром в своей схеме:
scalar MyVariant # Could be whatever!
Недостатком является то, что если вы хотите добавить более богатое поведение GraphQL к своим вариантам продукта, вам не повезло. Скаляры — это листовые узлы в дереве запросов.
Одиночный объект в объединении
Вы можете представить тип как union
обычных объектов type
s, а также создать type
s, которые представляют ваши одноэлементные параметры. Для синглтонов вам нужно добавить какое-то фиктивное поле, потому что типы объектов должны иметь хотя бы одно поле. Вы можете сделать поле именем типа, но оно уже доступно как __typename
для каждого type
. Мы решили просто сделать его нулевым, значение которого всегда равно null
:
union MyVariant = RealObject | Singleton1 | Singleton2
type RealObject {
field1: String
field2: Int
}
type Singleton1 {
singletonDummyField: String # ALWAYS `null`
}
type Singleton2 {
singletonDummyField: String # ALWAYS `null`
}
type Query {
data: MyVariant
}
# query looks like:
{
data {
myVariantType: __typename
... on RealObject {
field1
}
}
}
Таким образом, запрос для __typename
удовлетворяет потребности предоставить хотя бы одно поле в запросе для типа объекта. Вы бы никогда не запросили singletonDummyField
, и вы можете почти забыть, что он существует.
На вашем сервере GraphQL легко создать помощника, который материализует для вас одноэлементные типы — их единственными вариациями являются их имена и метаданные. Недостатком является то, что клиентские запросы получают стандартные шаблоны.
Интерфейс реализации объекта Singleton
Если идея фиктивного поля неприятна, и вы предпочитаете создать собственное поле типа, вы можете создать interface
, у которого явно есть поле типа, и использовать enum
для представления типов. Так:
enum MyVariantType {
REAL_OBJECT
SINGLETON1
SINGLETON2
}
interface MyVariant {
myVariantType: MyVariantType
}
type RealObject implements MyVariant {
myVariantType: MyVariantType
field1: String
field2: Int
}
type Singleton1 implements MyVariant {
myVariantType: MyVariantType
}
type Singleton2 implements MyVariant {
myVariantType: MyVariantType
}
type Query {
data: MyVariant
}
# query looks like:
{
data {
myVariantType
... on RealObject {
field1
}
}
}
Итак, здесь вы запрашиваете «настоящее» немета-поле myVariantType
, а у одноэлементных типов есть «настоящие» поля, даже если они избыточны по отношению к полю __typename
. Конечно, вы можете использовать подход __typename
, но какой в этом смысл. Недостатком здесь является то, что для реализации шаблона требуется гораздо больше шаблонов на стороне сервера. Но это, вероятно, тоже можно было бы учесть в помощнике, просто с немного большей сложностью.
Композиция
Вы можете закодировать синглтоны как enum
, содержащиеся в типе объекта, который существует исключительно для их содержания.
union MyVariant = RealObject | Singleton
type RealObject {
field1: String
field2: Int
}
type Singleton {
variation: SingletonVariation!
}
enum SingletonVariation {
SINGLETON1
SINGLETON2
}
type Query {
data: MyVariant
}
# query looks like:
{
data {
... on RealObject {
field1
}
... on Singleton {
variation
}
}
}
Преимущество этого заключается в том, что вы не прибегаете к самоанализу или избыточным полям. Недостатком здесь является то, что он группирует одноэлементные варианты отдельно от вариантов продукта таким образом, что это может быть бессмысленно. Другими словами, структура схемы является прагматичной для реализации в GraphQL, а не для представления данных.
Заключение
Выбрать свой яд. Насколько я знаю, нет отличного ответа на вопрос, как сделать это таким образом, чтобы он был полностью свободен от шаблонного кода или утечки абстракции.
person
acjay
schedule
22.12.2017