Реализация GraphQL на чистой архитектуре Golang

Чистая архитектура - одна из самых популярных архитектур в программировании. Он известен своим хорошим разделением между каждым слоем. Каждому слою не нужно знать о реализации другого слоя, поэтому мы можем легко изменить реализацию, не внося изменений на другом уровне. Если вы хотите узнать больше о чистой архитектуре, вы можете прочитать ее здесь, потому что я не буду подробно объяснять это в этом посте.

Сегодня я хочу попробовать реализовать GraphQL в коде моего друга о чистой архитектуре в Golang, вы можете прочитать его замечательный пост Пробуем чистую архитектуру на Golang. В этом посте автор использует REST API в качестве уровня доставки, теперь я попытаюсь добавить еще уровень HTTP, который использует GraphQL.

GraphQL

Что такое GraphQL? По определению с их сайта:

GraphQL - это язык запросов для API и среда выполнения для выполнения этих запросов с вашими существующими данными. GraphQL предоставляет полное и понятное описание данных в вашем API, дает клиентам возможность запрашивать именно то, что им нужно, и не более того, упрощает развитие API с течением времени и предоставляет мощные инструменты разработчика.

Другими словами, GraphQL - это язык запросов, позволяющий Клиенту получать только то, что они просят, без получения неиспользуемых данных. Итак, если у нас есть объект статьи, подобный этому:

{
   "id": "article-id",
   "title": "Indonesia won the award of best clean air of South East    Asia",
   "author": "Kurio"
}

И клиенту нужно только поле title, сервер вернет только:

{
   "title": "Indonesia won the award of best clean air of South East    Asia"
}

Схема GraphQL

Чтобы начать использовать GraphQL, нам нужно определить нашу схему на основе нашего слоя Model и Usecase. Итак, на основе этого github у нас есть структура статьи:

Мы можем сделать тип статьи в нашей схеме GraphQL следующим образом:

На GraphQL по умолчанию 5 скалярных типов, а это:

  • Int
  • Плавать
  • Нить
  • Логический
  • ID

Благодаря этому мы можем сопоставить наши типы данных Golang string и int64 со скалярными типами String и Int на GraphQL. Для типа Time на Golang GraphQL поддерживает создание пользовательских скалярных типов, поэтому нам нужно определить Time скалярные типы в схеме, которую мы написали.

После создания типа объекта нам нужно создать Query и Mutation для схемы GraphQL. Эти 2 типа будут созданы на основе Usecase интерфейса чистого кода архитектуры, вот код интерфейса usecase:

В чем разница между Query и Mutation в GraphQL? Для краткости Query используется для извлечения данных, а Mutation используется для изменения или создания данных на сервере. Итак, на основе usecaseинтерфейса мы можем написать запрос и мутацию следующим образом:

Вы, должно быть, спрашиваете,

Подождите, что такое ArticlesResult в строке FetchArticle? Раньше в схеме типов не было типа ArticlesResult! Почему бы вместо этого просто не вернуть список статей?

В функции FetchArticle нам нужна функция разбивки на страницы и курсора, поэтому мы не сможем получить все статьи по одному запросу. В GraphQL есть стандартный тип для реализации разбивки на страницы, на основе этой страницы нам нужно создать соединение (край) и информацию (pageInfo) текущего объекта, и именно поэтому нам нужно создать объект ArticlesResult. Вот тип схемы для ArticlesResult:

Тип Edge будет иметь cursor с типом String и node с типом Article. курсор - это значение соединения для текущей статьи с соседней статьей. Мы можем получить следующую статью с этим значением cursor.

Тип PageInfo будет иметь endCursor с типом String и hasNextPage с типом Boolean. endCursor - это значение последнего курсора текущих ребер, поэтому мы можем получить следующие ребра с этим значением. hasNextPage будет нашим флагом независимо от того, будет ли у этого списка краев следующая страница или нет.

Тип ArticlesResult будет иметь тип edges с массивом типа Edge, pageInfo с типом PageInfo и totalCount с типом Int. totalCount будет иметь значение общего количества элементов текущих ребер.

Все типы GraphQL (объект, мутация и схема) могут быть записаны в одном файле, поэтому вот файл schema.graphql:

Погрузитесь в GraphQL Go

После того, как схема уже определена, нам нужно преобразовать ее в код Голанга. Для этого урока мы будем использовать пакет graphql. Я решил использовать этот пакет из-за его модульности, поэтому мы можем использовать его с нашей архитектурой чистого кода.

Преобразование схемы типа объекта

Во-первых, нам нужно создать объект GraphQL на основе нашей схемы, вот код:

Подготовка резолвера

Чтобы иметь возможность использовать пакетную функцию распознавателя, нам нужно создать функцию, которая реализует FieldResolveFn. Мы можем просто создать функцию для каждого резолвера, но на этот раз мы создадим интерфейс, который будет иметь все функции Query и Mutation из схемы, чтобы его можно было инициировать с введенным уровнем сервиса статьи (и более легким тестированием).

Создание конструктора схемы

Нам нужно будет создать объект GraphQL со всем явно написанным типом. Я имею в виду, что мы должны создать объект для Mutation с объявленными UpdateArticle, StoreArticle и DeleteArticle. Предыдущий преобразователь будет внедрен в схему объекта GraphQL, чтобы иметь возможность использовать его метод. Вот код:

Реализация резолвера

Не забудьте написать реализацию интерфейса Resolver. Мы создадим внутреннюю структуру, которая реализует все необходимые методы интерфейса, а также введем уровень сервиса статьи, чтобы мы могли получить доступ к данным статьи.

Почему бы нам просто не внедрить слой репозитория статей напрямую? Это связано с тем, что мы рассматриваем преобразователь как еще один уровень доставки, уровень доставки не может напрямую обращаться к уровню репозитория, поэтому нам нужно использовать уровень сервиса.

  • FetchArticle
  • UpdateArticle

Я думаю, что будет хорошей практикой написать другую реализацию самостоятельно, чтобы вы могли изучить, какие еще варианты есть в пакете. Но в целом код будет выглядеть так:

Инициализация доставки GraphQL

И на последнем этапе нам нужно инициализировать нашу доставку GraphQL в файле main.go. Просто измените файл, как этот:

Мы используем пакет https://github.com/graphql-go/handler для создания обработчика HTTP GraphQL и передачи обработчика в функцию echo WrapHandler, но если вы не хотите его использовать, вы можете создать свой собственный обработчик следуя правилам https://graphql.org/learn/serving-over-http/.

Теперь у нашей go-clean-arch уже есть еще один уровень доставки, который использует GraphQL, просто запустите свое приложение и получите доступ к нему по пути /graphql. У нас также будет пользовательский интерфейс GraphiQL, чтобы попробовать запрос GraphQL.

Надеюсь, это руководство поможет вам понять GraphQL и полезность чистой архитектуры в Golang.

Вы можете увидеть полный код из моего Github здесь:



Удачного дня!

Благодарим Иман Туморанг за архитектуру чистого кода на Голанге, Бастиан Паскаль Ситуморанг, Резу Индру, Нугрохо Кахионо из Kurio, которые помогли мне реализовать GraphQL в Golang 😎