REST/CRUD. Я неправильно его готовлю? Часть 2

    Вступление


    В первой части я начал делиться своими наблюдениями по поводу реализации HTTP/GET в REST. В этой — попробуем рассмотреть вопросы версионирования и архитектуры. Приступим?

    Версионирование — то, без чего Ваш API бесполезен


    Помните прошлую публикацию?
    URL/URN/URI/resource/slug — все эти понятия мы рассмотрели. Чего же боле?
    Вся адресация из первой статьи — синтетическая, никакого отношения к реальному проекту она не имеет.
    ТАК ПИСАТЬ API НЕЛЬЗЯ!
    Нельзя потому что в URI напрочь отсутствует такая важная вещь как версионирование.
    Почему URI? Почему не URL? Почему не URN? Да потому что наш API предоставляет доступ к состояниям объектов внутри коллекций. Т.е. нам не принципиально какой URL (читай «на каком сервере») или какой URN (читай «какой бекенд») будут у объекта, нам важно знать оъект какой версии мы используем.
    В разных источниках мелькает тема версионирования API, но сфрмулирована, на мой взгляд, она не очень удачно. Считается, что, версионирование позволит не смешивать разные реализации API (т.е. версии ассоциируют с URL/URN).
    А сейчас откровение: А кто запретил демону (URN), запущенному на сервере (URL), обслуживающему коллекцию (resource), версии v2 принимать объекты версии v1? От того что объект имеет версию v1 он перестал быть валидным? Объект пользователя перестал быть пользователем? Непонятно? Ок. Давайте по порядку.

    Архитектура


    Из материалов википедии в разделе «архитектура REST» есть следующие строки:
    Клиент может взаимодействовать не напрямую с сервером, а с произвольным количеством промежуточных узлов. При этом клиент может не знать о существовании промежуточных узлов, за исключением случаев передачи конфиденциальной информации.

    там же есть перечень целей которые пытаемся достич
    • масштабируемость
    • портативность компонентов
    • легкость внесения изменений
    • способность эволюционировать


    Чего хотел добиться автор? Нет, «мир во всем мире» — это понятно, но все же…

    Попробуем декомпозировать задачу и воспользуемся «типичными» решениями горизонтального масштабирования:
    1. Множество серверов обслуживающих клиентские запросы реализуем через DNS
    2. В каждом сервере имеем балансировку по множествам бекендов на разных внутренних серверах
    3. В каждом бекенде от 1 до n коллекций


    А теперь вопрос на миллион: как коллекции общаются друг с другом? Логичен вопрос «ЗАЧЕМ?», но готов ответить «синтетическим» примером:
    Дано:
    • Сервер #1 с бекендом, в котором есть коллекция users
    • Сервер #2 с бекендом, в котором есть коллекция groups

    Задача: При добавлении пользователя в коллекцию groups — проверять что пользователь присутствует в коллекции users. При получении пользователей из groups определять все еще существует этот пользователь в коллекции users, и, если нет — удалять его из коллекции groups и не передавать пользователю информацию о нем.

    Оставим сейчас тонкости URL/URN/URI/etc и того что по ним передается. Вопрос в том, как в распределенной системе добиться консистентного состояния? Первое что приходит в голову — использовать тот же REST до соседней коллекции, но где взять отказоустойчивый сервер с кешированием и балансировкой нагрузки?! СТОП! Так все уже есть! Бери и используй!

    Хм… ок, есть у нас масштабируемый http-кластер (да, я знаю что это не кластер в строгом смысле этого слова), а причем тут версионирование? А притом, что, не рационально в распределенной системе, которая поддерживает коммуникации между участниками, городить огород со стеком старого API (читай «делать еще один кластер по меньше»). И, в ряде случаев, таскать бекенды между версиями. Например в версии 2 изменилась только коллекция user и добавилась contacts. Как groups переедет в версию 2?

    Критикам


    Предрекая вопросы попытаюсь внести ясность по некоторым пунктам.

    О чем вобще речь? Что за бред?
    Речь о том, что, версионирование — свойство коллекции а не API. В противном случае — никакого масштабирования.

    У Вас проблема в архитектуре. Должно быть как то так: /groups/group_name/users/user_name
    Да, так оно и есть, но:
    … до тех пор пока пользователь может находться в одной группе, в противном случае у вас по 2-ум и более URI будет возвращаться один и тот же объект.
    … до тех пор пока у пользователя обязана быть группа, в противном случае придется вводить фейковые группы (default, global, system, all — правда информативненько?)

    REST для коммуникаций в распределенной системе? Вы в своем уме?!
    Не беспокойтесь, я знаю что «белые люди» для этого используют асинхронные кластеризуемые message-брокеры поверх транспортных каналов с гарантированной полосой и прочих прелестей корпоративных систем. Это «синтетический» пример, да и не такой уж и плохой в рамках 3-5 серверов.

    Это все что у Вас есть по версионированию?
    О, нет! Я только начал!

    P.S. Друзья, на часах 3:00, а значит пора спать. Прошу всех высказываться в коментариях. Без Ваших фидбеков писать ооочень тяжело.
    • –4
    • 4,6k
    • 2
    Поделиться публикацией

    Комментарии 2

      +3
      Замечательный гид по разработке API github.com/interagent/http-api-design. Все уже достаточно хорошо описано и используется в API heroku.
        +3
        Автор, вам стоит поработать над стилем изложения информации: рвано, эмоционально, разрозненно, неожиданно заканчивается. Если на часах 3 ночи, то это не значит, что пора публиковать статью, это значит, что пора нажать кнопку «в черновики» и пойти спать, а утром, со свежей головой, еще раз прочитать/дописать/поправить.

        Прочитав две статьи не понял почему их две? о_О Прочитав первую, я, еще как-то, понял вашу мысль, прочитав вторую не понял ничего — обрывки мыслей и информации про версионирование. Да и заголовок странноват…

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое