Слайд 1Переход от REST API к GraphQL на примере реальных проектов
Антон
Морев, Wormsoft
Слайд 2Антон Морев
Приложения
Сайты
Системы автоматизации (CRM, ERP)
Маркетинг
Сопровождение проектов
IT-Директор
Оптимизация процессов разработки
Review ключевых проектов
Слайд 3DATABASE
CACHE
OTHER API
QUEUE
… ETC
КЛИЕНТ
СЕРВЕР
Взаимодействие с сервером
API
Слайд 4POST /order
GET /goods
Стандартное решение на REST
Слайд 6“Я не хочу писать документацию,
я хочу писать код”
© “Senior”
Developer
№1
БЕЛЫЙ ЛИСТ
Слайд 7спроси у Ивана - он знает где у нас это
в
каком поле у нас текущий статус?
№2
№2
МУДРЕЦ
Слайд 8«Мы храним документацию в docx файлах под git
со всеми
JSON ответами и запросами»
№3
WORD, EXСEL , GOOGLEDOC
Слайд 9Инструменты для документации (swagger и тд)
№4
Слайд 11Количество запросов
HEADER
FOOTER
PRODUCT CARD
REVIEWS
getDeliveryData
getHeaderData
getFooterData
getProductCardData
getReviewsData
CONNECTED PRODUCTS
Слайд 12КАТАЛОГ ТОВАРОВ
ОТЗЫВЫ
КОРЗИНА
КАРТОЧКА ТОВАРА
ИСТОРИЯ ЗАКАЗОВ
ТОВАР
Слайд 13{
"id": 123,
"title": "Bentley For Men",
"price": 100,
"connectedProducts":
[],
"photos": [],
"countInBasket": 3,
"reviews": [],
"variants": []
}
"description":
"Лучшие духи",
"brand": "Bentley",
GET /allRoundProduct?id=123
Слайд 15{
"id": 123,
"title": "",
"price": "",
"description": "",
"brand":
"",
"photos": []
}
GET /product/card?id=123
Слайд 16[
{
"id": 123,
"title": "",
"price":
100,
"photo": {}
}
]
GET /product/connected?id=123
Слайд 17[
{
"id": 123,
"title": "",
"photo":
[],
"rating": 3,
"price": 100
}
]
GET
/product/list
Слайд 18{
"commonInfo": {},
"cardInfo": {},
"listInfo": {},
"basketInfo": {}
}
ИЕРАРХИЧЕСКАЯ МОДЕЛЬ
GET
/product/full?id=123
Слайд 19Интернет-магазин Решения на REST
ЯЗЫК ЗАПРОСОВ
только cardInfo и basketInfo
product/full?id=1&fields=cardInfo,basketInfo
API
Интернет-магазин. Решения на
REST
Слайд 20getModel?id=1&fields=title,
description,price
getModel?id=1&fields=title,price,
connectedProducts.title,
connectedProducts.description
getModel?id=1&fields=
user.id,reviews.date,reviews.text,
title,description,
rating,price,
reviews.user.name,reviews.
connectedProducts.title,
connectedProducts.price, connectedProducts.rating,
photos.src,photos.alt,
dicsount.rate,discount.amount
Слайд 21МНОГО
ЗАПРОСОВ
(удобно)
ОДИН
ЗАПРОС
(неудобно)
Интернет-магазин. Решения на REST
Слайд 22Можно выбирать что именно мы хотим получить
Несколько сущностей в одном
запросе
Автоматическая документация
Интернет-магазин Желаемое решение
Интернет магазин. Желаемое решение
Слайд 24ОПИСАНИЕ СХЕМЫ
type Product {
id: ID!
title: String
description: String
}
type
Query {
product (id: ID!): Product
}
Слайд 25ЗАПРОС
query {
product (id: 123)
{
title
description
}
}
Слайд 26{
"product": {
"title": "Духи",
"description":"Описание товара…"
}
}
ОТВЕТ
Слайд 27type Product {
id: ID
title: String
description: String
variants: [ProductVariant]
}
ВАРИАНТЫ ТОВАРА
Слайд 28query {
product (id: 123) {
title
variants {
id
title
}
}
}
ПОЛУЧЕНИЯ ПОЛЯ VARIANTS
Слайд 29{
"title": "Chanel Chance Eau Tendre",
"variants": [
{
"id": 1,
"title": "Подарок, 200мл"
},
{
"id": 2,
"title": "Пробник, 1.5мл "
}
]
}
ОТВЕТ
Слайд 30type ProductVariant {
id: ID
title: String
price: Float
priceDiscount: Float
image: ImageModel
}
ИНТЕРНЕТ-МАГАЗИН РЕШЕНИЯ НА
GRAPHQL
Слайд 31ЗАПРОС ДЛЯ СТРАНИЦЫ
query {
header {
#header fields
}
product (id: 123) {
#product fields
}
footer {
#footer fields
}
}
Слайд 33Выбор/Создание запроса
Добавление нужного
поля в response
Реализация логики
получения поля
Расширение документации
BACKEND ДО
Процесс
внедрения нового свойства в сущность товара
Слайд 34BACKEND ПОСЛЕ
Процесс внедрения нового свойства в сущность товара
Выбор/Создание запроса
Добавление нужного
поля
в тип
Реализация логики
получения поля
Расширение документации
Процесс внедрения нового свойства в сущность
товара
Слайд 35FRONTEND ДО
Внедрение GraphQL Интернет-магазин
Выяснение в каком запросе было обновление
Изучение документации
Использование
поля
Новый endpoint/расширение старого
Процесс внедрения нового свойства в сущность товара
Слайд 36FRONTEND ПОСЛЕ
Добавление нужного поля в GQL запрос за сущностью
Изучение документации
Использование
поля
Новый endpoint/расширение старого
Процесс внедрения нового свойства в сущность товара
Слайд 37ВНЕДРЕНИЕ GRAPHQL
РАЗРАБОТКА С МИКРОСЕРВИСАМИ
Слайд 38Банк криптовалюты
Интерфейс
КРИПТОВАЛЮТНЫЕ БИРЖИ
WALLET RPC
ЯДРО СИСТЕМЫ
Слайд 39PHP Yii2
REST
API Module
Wallet RPC (C++)
Exchange API
Auth module
Deposit module
Payments module
Email module
ИНТЕРФЕЙС
Слайд 40class TransactionsController extends Controller
{
public function actionIndex($page = 1,
$type = null)
{
$userId =
Yii::$app->user->id;
return $this->transactionsRepository
->getTransactions($userId, $page, $type);
}
}
Слайд 41Интерфейс
Email service (GoLang)
Money module
(PHP)
Exchanges Services
(PHP, NodeJS)
Chat service
(NodeJS)
API Core Server (PHP
Yii2)
Docs
Docs
Docs
Docs
Слайд 42СОБСТВЕННЫЙ РЕПОЗИТОРИЙ
ПРЯМОЙ ЗАПРОС В БД
'resolve' => function($value, $args, $context, ResolveInfo
$info) {
return DB::getInstance()->select('SELECT * from products');
},
'resolve' => function($value,
$args, $context, ResolveInfo $info) {
//Получение любой информации
}
'resolve' => function($value, $args, $context, ResolveInfo $info) {
return $context->productRepository->getProducts($args['page']);
}
Слайд 43GET
query {
/user/
payments
/list
user {
payments {
list {
id
amount
}
}
}
}
Слайд 44GET
/notifications
/user
/payments
/list
GET
/user
/info
GET
/user
GET
/user
/system
/status
/list
query {
user {
payments {
list {
id
amount
}
}
info {
name
money
}
system {
status
list {
}
notifications {
text
}
}
}
}
id
Слайд 45ВНЕДРЕНИЕ GRAPHQL
ОПТИМИЗАЦИЯ РАЗРАБОТКИ
Слайд 46сущности
сбор данных с API сервера
управление примитивными CRUD сущностями
Внедрение GraphQL. Админ-панель
SITE
API
Server
Admin Panel
отображение данных
Frontend логика
бизнес логика
обработка заявок
система оповещений
управления бизнес логикой (формы)
Слайд 47Внедрение GraphQL. Админ-панель
Описание сущности
GraphQL схема
resolvers для каждого поля каждой сущности
интерфейс
админ-панели
автоформирование
Слайд 48{
“type”: “input_text”,
“label”: “Название”
“name”: “title”
}
metro {
list
{
items {
title
}
}
}
Слайд 49query {
offices {
title
photoSrc
address
metro {
list {
title
}
}
shortDescription
price {
month
}
}
}
Слайд 50Изменение в процессе добавления/ изменения примитивной сущности
BACKEND
ДО
ПОСЛЕ
Разработка Логики хранения данных
Разработка
логики CRUD
Разработка интерфейса управления
Добавление/ расширение запроса
Обновление документации API
Обновление конфигурации
Разработка логики
CRUD
Разработка интерфейса управления
Добавление/ расширение запроса
Обновление документации API
Слайд 51FRONTEND
ДО
ПОСЛЕ
Изучение документации
Добавление нового запроса
Обработка данных
Изучение документации
Расширение Query
Обработка данных
Изменение в процессе
добавления/ изменения примитивной сущности
Слайд 53GraphQL. Мутации
mutation {
office {
update (id:123, model: {title:
"newTitle"})
}
}
Слайд 54GraphQL. Обновление данных
{
taskModule {
task
(id: 123) {
mainInfo (
title: “title”,
description: “description”
)
}
}
}
POST /taskModule/task/mainInfo?id=123
{
title: “title”,
description: “description”
}
Слайд 57Обычный запрос в REST
API
Запрос
Парсинг запроса
Определение контроллера
Запуск контроллера
Получение результата
Ответ
Слайд 58API
…
Как работать с GraphQL. Асинхронность
Запрос
Ответ
Парсинг запроса
Определение резолверов
Запуск резолвера
Запуск резолвера
Запуск резолвера
Подготовка
ответа
Слайд 59За чем следить в GraphQL?
N+1
Сложность запросов
Сложность контроля
Кэширование
Порог вхождения
Асинхронность
Слайд 60query {
task {
list {
id
title
}
}
}
Особенности
GraphQL. N+1
Слайд 61query {
task {
list {
id
title
subtask {
title
}
}
}
}
Особенности GraphQL. N+1
Слайд 62Особенности GraphQL. N+1
'resolve' => function ($root) {
Loader::add($root->id);
return new Deferred(
function () use ($root) {
Loader::prepareData();
return Loader::get($root->id);
}
);
},
]
Слайд 63Как работать с GraphQL. Сложность запроса
task {
subTask {
subTask {
subTask {
subTask
{
id
}
}
}
}
}
Слайд 641. task {
2. subTask {
3. subTask {
4. subTask {
5. subTask
{
6. id
}
}
}
}
}
ОГРАНИЧЕНИЕ ГЛУБИНЫ ЗАПРОСА
Query Depth < 4
Слайд 653*i5
1. task {
subTask {
subTask {
subTask {
subTask
{
id
}
}
}
}
}
ОГРАНИЧЕНИЕ СЛОЖНОСТИ ЗАПРОСА
Query Complexity < 40
3*i2
3*i3
3*i4
3*i6
Слайд 66Как работать с GraphQL. Кэширование
Кэширование на уровне web-сервера
GET /api/info
cache
КЛИЕНТ
Приложение
Слайд 67Как работать с GraphQL. Кэширование
POST /graphql
Кешировать на основании body
КЛИЕНТ
Приложение
Кэширование на
уровне web-сервера
Слайд 68Как работать с GraphQL. Кэширование
GET /api/info
cache
public function actionInfo()
{
$query = ''; //фиксированный запрос
return GraphQLHandler::handleQuery($query);
}
Клиент
Приложение
Кэширование на уровне web-сервера
Слайд 69Зачем GraphQL, если и так сойдет?
Внедрение GraphQL. Сложность адаптации
Слайд 70А почему не GraphQL?
Внедрение GraphQL. Сложность адаптации
Слайд 71API
…
Как работать с GraphQL. Асинхронность
2
Запрос
Ответ
Парсинг запроса
Определение резолверов
Запуск резолвера
Запуск резолвера
Запуск резолвера
1
2
N
Подготовка
ответа
Слайд 72Как работать с GraphQL. Асинхронность
REST
API
GET user/info
GET account/money
GET something/more
Слайд 73Инициализация запроса ~ от 30мс (PHP)
Как работать с GraphQL. Асинхронность
Слайд 74Как работать с GraphQL. Долгие поля
Слайд 75Один тяжелый или много легких?
Внедрение GraphQL. Постановка вопроса
Слайд 77Нагрузочный тест. Инструментарий
Слайд 78SQLITE
Nuxt.JS
PHP-SLIM
Нагрузочный тест
API
Слайд 79Redis
Node.JS
PHP-SLIM
Нагрузочный тест
API
Слайд 80Нагрузочный тест. Инструментарий
ОЗУ: 1ГБ
SSD: 30ГБ
CPU: 1
Backend
PHP 7.2
Slim Framework
webonyx/graphql-php
Frontend
Node.JS 10.16
Express
Axios
HTTP Client
Слайд 81Нагрузочный тест. Условия
30 одновременных пользователей
Суммарное количество запросов: 200 каждый
Слайд 82Кейс 1. Список товаров
…
Товар 1
Товар 2
Товар 3
Товар N
Сравнение получение одинакового
количества
данных
Проверка влияния N+1 на результат
Слайд 83Кейс 1. Список товаров
Среднее время ответа (мс)
Слайд 84Кейс 2. Карточка товара без связей
Описание товара
Фото
товара
Характеристики товара
Слайд 85Кейс 2. Карточка товара без связей
Среднее время ответа (мс)
Слайд 86Кейс 3. Карточка со связями
Фото
товара
Описание
товара
Характеристики товара
Отзывы о товаре
Связанные
товары
Рейтинг пользователя
Рейтинг товара
Наличие в корзине
Слайд 87Кейс 3. Карточка со связями
Среднее время ответа (мс)
Слайд 88Небольшой вывод
Стоит ли подключать GraphQL в реальные проекты?
Да, но всегда
помнить о:
N+1
Сложностях кеширования
N+1
Сложности запроса
N+1
Слайд 89Интерфейс
Работа с подписками
curl export
CPU 20%
от создателей GraphQL
интеграция с кодом проекта
Как
работать с GraphQL. Инструменты
JS-GraphQL
GraphiQL
Слайд 90Как работать с GraphQL. Библиотеки
Frontend
Backend
graphql-go/graphql
webonyx/graphql-php
graphql/graphql-js
vuejs/vue-apollo
apollographql/react-apollo
Слайд 91Когда GraphQL усложняет процессы
В некоторых MVP
В системах отчетности и аналитики
В
общих сервисах аутентификации
В командах где не знают GraphQL
1
2
3
4
Слайд 92Антон Морев
Благодарю за внимание!
amorev
wormsoft.ru
@amorev