Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Что не так с REST

Что не так с REST

Иван Гришаев

Deep Refactoring

August 31, 2017
Tweet

More Decks by Deep Refactoring

Other Decks in Education

Transcript

  1. Что не так с REST
    Иван Гришаев, Глубокий рефакторинг

    View Slide

  2. API
    Машинный интерфейс
    Автоматизация
    —> Машина —> Машина -X- Человек -X- Машина —> Машина

    View Slide

  3. API
    Выбор оператора
    Тип банковской карты
    Справки, документооборот
    Просто нет нужной апишки

    View Slide

  4. Минутка истории
    Первые API
    XML/SOAP
    XML/RPC
    WSDL

    View Slide

  5. Минутка истории
    Интеграция с 1C
    Python/Suds
    JSON inside XML/SOAP
    https://github.com/igrishaev/1c-json

    View Slide

  6. REST
    Де-факто в индустрии
    Все фреймворки по REST
    Масса утилит (Swagger, RAML)
    Сервисы/сайты-API
    Священная корова

    View Slide

  7. REST
    Не факт, что было задумано именно так
    Так уж вышло
    Вложено много сил
    Все ли мы делаем правильно?

    View Slide

  8. Неконкретика
    Что такое REST?
    Representational state transfer
    Method + Resource + ID

    View Slide

  9. Неконкретика
    Как передать версию?
    /api/v1/users
    /api/users?version=1
    "Version": "1"
    {"name": "Ivan", "version": 1}
    (Как вообще версионировать? И нужно ли?)

    View Slide

  10. Неконкретика
    CRUD are not enough
    PUT vs POST vs PATCH
    Не отражает бизнес-логику

    View Slide

  11. Роспуск группы
    DELETE /groups/42
    Группа не удаляется
    Затрагиваются пользователи
    Пишется история

    View Slide

  12. Дать пользователю скидку
    POST /users/42/discount
    PUT /items/42/users/1
    PATCH /promo {"user_id": 42, "discount": 10}

    View Slide

  13. Неконкретика
    В крупных фирмах спеки
    Google: Resource Oriented Design
    У вас стартап, вам некогда

    View Slide

  14. Данные размазаны по всему запросу
    GET /api/users/?active=true HTTP/1.1
    Version: 1
    Authentication: Basic Base64String==

    View Slide

  15. Данные размазаны по всему запросу
    PUT /api/users/1 HTTP/1.1
    Version: 1
    Authentication: Basic Base64String==
    {"name": "Ivan", "age": 31}

    View Slide

  16. Данные размазаны по всему запросу
    Нет смысла
    Слишком большой скоуп
    Такой Request трудно построить
    Только для веб-сервера верхнего уровня
    Чтение по частям

    View Slide

  17. Неконкретика
    Когда GET, обрабатываю query string
    Когда другой, тело запроса
    Нет одного источника данных

    View Slide

  18. Какая разница?
    import boto
    import time
    s3 = boto.connect_s3()
    bucket = s3.create_bucket('boto-demo-%s' % int(time.time()))
    key = bucket.new_key('mykey')
    key.set_contents_from_string("Hello World!")
    print key.get_contents_as_string()
    key.delete()
    bucket.delete()

    View Slide

  19. Какая разница?
    String cleanBucketName = bucketName.replaceAll("/$", "");
    String uri = GCS_URI + cleanBucketName;
    HttpRequestFactory requestFactory = HTTP_TRANSPORT.createRequestFactory(credential);
    GenericUrl url = new GenericUrl(uri);
    HttpRequest request = requestFactory.buildGetRequest(url);
    HttpResponse response = request.execute();
    String content = response.parseAsString();

    View Slide

  20. Кеш? Не нужно
    Чаще всего кеширование как раз не нужно
    Костыли с random query arg
    Так и быть, разрешить GET + query string
    Varnish

    View Slide

  21. Зависимость от классов
    from django.conf.urls import url, include
    from django.contrib.auth.models import User
    from rest_framework import routers, serializers, viewsets
    class UserRest(BaseRestClass):
    def on_get(self, request):
    ...
    def on_put(self, request):
    ...
    def on_post(self, request):
    ...
    Как инициализировать класс?
    Как инициализировать Request?
    Как переиспользовать on_put?

    View Slide

  22. Swagger
    Интерфейс
    Запрещает ошибки, а нам нужно
    Не сохраняет сессии (Postman)
    Убог
    Вал зависимостей

    View Slide

  23. Как нужно
    Главное — данные
    Data-oriented programming
    Мыслить в терминах бизнес-логики
    Alan Kay and Rich Hickey about "data"

    View Slide

  24. Request vs Data
    Запрос — это не данные, а состояние
    Огромный скоуп (сокет, сессия, ...)
    Нельзя сохранить
    Данные — постоянны

    View Slide

  25. SEA — single endpoint API
    Максимально абстрагироваться от транспорта
    Действие и аргументы
    Все в одном месте

    View Slide

  26. SEA — single endpoint API
    Действие — что делать
    На сервере словарь "действие" ⇒ функция
    Одна функция-диспатчер

    View Slide

  27. SEA — single endpoint API
    {
    "action": "list_users",
    "active_only": true, # optional
    "order_by": "name", # optional
    "version": 1,
    "performer_id": 42,
    "signature": "671345234y6234"
    }

    View Slide

  28. SEA — single endpoint API
    Метаданные
    В словаре не только функции, но и схемы, права и тд

    View Slide

  29. SEA — single endpoint API
    actions = {
    "get_user": {
    "handler": api.get_user,
    "schema_in": schemas.get_user_in,
    "schema_out": schemas.get_user_out,
    "role": "admin",
    "doc": "Some API description",
    "added": "1.4.2",
    }
    }

    View Slide

  30. Одна точка входа
    /users/
    /users/42
    /users/42/orders/
    /order/
    /order/42
    /customer/
    /customers/42
    /customers/42/orders/

    View Slide

  31. Одна точка входа
    А теперь еще клиент!
    var apiRoutes = {
    get_user: "/users/:id",
    list_users: "/users/",
    user_orders: "/users/:id/orders",
    ...
    }

    View Slide

  32. Одна точка входа
    /api {"action": "create_user", "name": "Ivan", "age": 31}
    /api {"action": "update_user", "user_id": 1, "name": "Juah"}
    /api {"action": "delete_order", "order_id": 42}
    ...
    1 функция на сервере
    1 функция на клиенте

    View Slide

  33. Масштабируется
    Не нужно писать новый класс
    Не нужно добавлять новый урл
    Просто новый ключ в словаре
    Версионирование по ключам (fallback)

    View Slide

  34. Просто, понятно
    Данные
    Словари
    Функции

    View Slide

  35. API знает все о себе
    Список всех апишек с доками
    Рендер документации как нужно нам
    Апишка, которая возврашает апишки, схемы, метаданные

    View Slide

  36. API знает все о себе
    список апишек
    {"action": "list_api"}
    документация к апишке
    {"action": "get_doc", "target": "list_api"}
    схемы данных
    {"action": "get_schemas", "target": "get_doc"}

    View Slide

  37. Сценарии
    {...}, {...}, {...} в файле, в коде
    VS
    GET /users/42 HTTP/1.1
    POST /users/ HTTP/1.1
    {...}
    PUT /users/42 HTTP/1.1
    {...}

    View Slide

  38. Примеры
    Вконтакте
    https://api.vk.com/method/users.get?user_id=210700286&v=5.52
    https://api.vk.com/method/pages.getTitles?v=5.51
    https://api.vk.com/method/wall.deleteComment?comment_id=123123&v=5.51

    View Slide

  39. Примеры
    Наш стартап
    {"action": "users/get", "user_id": 42}
    {"action": "users/update", "user_id": 42, "name": "Juan"}

    View Slide

  40. Примеры
    actions = {
    "get_user": api_get_user,
    "update_user": api_update_user,
    "list_users": api_list_users
    }
    def api_update_user(data):
    user_id = data.pop("user_id")
    Users.objects.update(data).filter(id=user_id)
    return response_ok()

    View Slide

  41. Примеры
    (def action-rules
    {:displayfixtures/get-nearby
    {:handler #'nearby-displayfixtures
    :schema-in s/CQRSNearbyDisplayfixturesIn
    :schema-out s/CQRSNearbyDisplayfixturesOut}
    :displayfixtures/get-all-displayfixtures
    {:handler #'get-all-displayfixtures
    :schema-in s/CQRSParams
    :schema-out s/CQRSGetAllDisplayfixturesOut}

    View Slide

  42. Примеры
    (defmulti api :action)
    (defmethod api :users/get
    [{user_id :id}] ...)
    (defmethod api :users/update
    [params] ...)
    (defmethod api :default
    [params]
    {:status 404})

    View Slide

  43. Преткнования
    Это не REST!!!
    У нас так не принято
    Утилиты
    Жалко потраченных сил

    View Slide

  44. Что это дает
    Независимость от транспорта
    Очереди, таски, Async
    Сценарии, обстрелы
    Масштабируемость
    Прозрачность
    Критичный взгляд

    View Slide

  45. Ссылки
    From REST to CQRS
    https://www.youtube.com/watch?v=qDNPQo9UmJA
    https://martinfowler.com/bliki/CQRS.html
    https://news.ycombinator.com/item?id=11945722

    View Slide

  46. Вопросы

    View Slide