API plus fat client – a new paradigm in web development?

API plus fat client – a new paradigm in web development?

Any given major web service has its own API (Facebook, Flickr, Amazon, Vkontakte, etc.). But do smaller projects need one? What are the advantages of separating the front-end from the back-end through an API?

Technology is quickly advancing, as is Javascript— not the language used for animating webpage elements, but the powerful development tool. Few people are surprised by the need to use frameworks and libraries like AngularJS or Backbon e. Single-page web applications are more actively competing with mobile applications due to how light they are and their simplicity.

In this presentation, the advantages and features of developing using an API are covered:
1. The basic features and business and end-user advantages of single-page applications.
2. How to change development speed. After interfacing with an API, all system modules can process in parallel and independent of one another. This approach allows you to better concentrate on each part of the system without disrupting major development components.
3. Automated API testing methods.
4. Non-obvious advantages of single-page application search engine optimization.

Who: Andrey Lebedev
Where: RIT 2013
When: 22-23 April, 2013

API плюс толстый клиент – новая парадигма веб-разработки?

Андрей Лебедев: В последнее время бизнес-заказчики приходят к нам со следующими требованиями.

Первое требование – сделать веб-сервис, в котором был бы обширный доступ к данным различных клиентов (веб-сайт, мобильные приложения и, допустим, некоторые публичные API).

Второе требование – это время отклика интерфейса. Это довольно существенное в наше время конкурентное преимущество. Быстрый интерфейс – это удобно, это хорошо.

Есть такое утверждение Якоба Нильсена о том, что существует три критерия оценки веб-интерфейса по скорости:
Первое – это когда время отклика веб-интерфейса и вообще интерфейса составляет 100 миллисекунд – тогда пользователь не замечает обращения к серверу, он считает, что все данные локальные, все события в программе происходят локально, в режиме реального времени.

Второе – время отклика 1 секунда – это еще комфортные условия для пользователя, но он понимает, что происходят какие-то операции, обращение к серверу, сайт открывается, что-то крутится, он ещё должен загрузиться – но в целом терпимо.

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

Он (Нильсен) предлагает использовать различные элементы UI, чтобы отобразить время загрузки. Но бизнес стремится к тому, чтобы время отклика приложений стремилось к 100 миллисекундам, чтобы это было как будто бы десктоп приложение.

Рассмотрим традиционную схему генерации веб-страниц на сервере, которую вы все наверняка хорошо знаете.

Есть браузер, есть веб-сервер и есть база данных. Тут я бы поподробнее немного описал бы ещё структуру серверного приложения, потому что мы потом перейдём к Single Page Application (SPA), там примерно похожая схема на клиенте.

Что хотелось бы сказать сразу про эту схему, в контексте высказываний Нильсена о скорости интерфейса. В чём проблема? Проблема этой классической, традиционной схемы в том, что веб-страницу нужно сгенерить на сервере, несмотря на то, что данные, допустим, лежат в кеше, и в шаблоне они уже скомпилённые – всё равно придётся затрачивать какое-то количество CPU и дисковой активности, чтобы собрать этот HTML, отправить клиенту. А браузер должен HTML проанализировать, построить DOM дерево, найти все ресурсы, которые связаны с HTML и необходимы для дальнейшей работы приложения, загрузить их, если их нет в кеше, а если есть в кеше – вытащить скрипты, перекомпилить. Получается, что даже если ваш веб-сервер достаточно быстро отдаёт HTML-код (со скоростью 20-30 мс), всё равно (нагрузку на сервере опустим) клиент будет отображать данные с какой-то задержкой. Задержка порой весьма существенна, если количество скриптов достаточно большое и какая-то логика уже есть.

За последние 10 лет качество серверного кода весьма сильно увеличилось, и серверные системы достаточно хорошо развились. Они и тестируемые, и быстрые – куча разных способов построения – всё замечательно.

Забегая вперёд, скажу, что, по моим ощущениям, по тому, что я вижу, 80% сайтов, имеют такой достаточно «рыхлый» код на клиенте, на JavaScript, поэтому SPA и использование js-фреймворков в каком-то смысле эту проблему решает.

Итак, два требования клиентов:
1. Бизнес – доступ к данным с различных устройств.
2. Скорость отклика.

По поводу скорости отклика немного объясню. Как бы мы ни старались, всё равно будут задержки. И как-то надо решить вопрос с доступом к данным. Можно схему расширить и добавить в неё ещё дополнительно роутер, контролер и представление, модель можно оставить старую – и сделать доступ по REST API дополнительно к существующему уже веб-серверу, который отдаёт HTML.

Схема рабочая, многие её используют. В данной схеме получается, что у нас на сервере две бизнес-логики, и нам эти две бизнес-логики придётся обслуживать. И в этом смысле это не очень хороший вариант.

Подводя итог классической, традиционной схеме, можно отметить ее плюсы – относительная простота разработки (в серверном фреймворкинге уже достаточно хорошо развиты); поисковая видимость (к ней вернёмся чуть попозже) без «костылей», то есть HTML сгенерился – и его увидит поисковый робот.

А минусы – это то, что время отклика пользовательского интерфейса, как мы говорили, не совсем устраивает, можно сделать побыстрее. И сильная связанность данных с представлениями – в том смысле, что HTML приходит с данными, и, чтобы отдельно выделить данные для мобильных устройств, придётся сделать ещё один дополнительный интерфейс, и серверную часть дорабатывать серьёзно.

Есть новая парадигма, новая концепция – как угодно можно называть – которая набирает обороты в последнее время. Хотелось бы её рассмотреть. Рассмотрим концепцию одностраничного веб-приложения, как оно работает.

Тема доклада – «толстый» клиент. «Толстый» клиент значит, что бизнес-логика находится на клиенте. По сравнению с традиционной схемой, здесь бизнес-логика на сервере никуда не делась, единственное, что сервер – интерфейс у него API – даёт исключительно данные. И этих данных будет меньше по объему и их можно отдавать в любом формате, но, скорее всего, интерфейс будет JSON, он более компактный, но дело даже не в этом. Само количество данных будет меньшим, потому что их будет запрашивать сам клиент (браузер). Но давайте поподробнее, как всё происходит.

Изначально браузер обращается к веб-серверу и, как обычно, загружает с него статику: HTML, набор javascript-модулей, CSS и всё остальное оформление. Javascript имеет структуру, MVC-структурой её сложно назвать, потому что контроля логики как такового там нет в большинстве фреймворков. Разработчики многих js-фреймворков называют или пытаются называть контроллером некоторые модули, но на самом деле там есть модели и есть представления. Основная особенность, в отличие от традиционной загрузки веб-сайта, веб-страницы с сервера, заключается в том, что на клиента приходит изначально HTML как шаблон, нет никаких данных, они пустые. А данные подгружаются уже Javascript’ом и рендерятся на клиенте, формируется HTML и отображается страница.

В чём плюс данного подхода? Плюс данного подхода заключается в следующем. Во-первых, у вас единый сервер, у вас единый интерфейс для доступа к данным. Во-вторых, у вас бизнес-логика вынесена на клиента, и, соответственно, на сервер чуть поменьше – это связано с разделением HTML. С другой стороны, этот интерфейс можно использовать для доступа любых прочих клиентов, таких, как мобильные клиенты-приложения, и отдавать на сторону использование данных.

Также плюс этого подхода заключается в том, что изначально загружено веб-приложения – HTML, Javascript. При переходе по веб-страницам, грубо говоря, по всему приложению, вся страница заново не загружается с веб-сервера, а загружаются только те данные, которые необходимо поменять – исключительно данные. Используются шаблоны, которые уже есть на клиенте, генерятся фрагменты HTML-кода, и изменяются только фрагменты страниц. Соответственно, это происходит достаточно быстро. Более того, если какие-то части страниц уже были отрисованы, то есть эти данные хранятся в javascript-объекте, соответственно, никакого обращения к веб-серверу не произойдёт, они возьмутся из объекта, из памяти, это происходит практически мгновенно.

Плюсы:

1. Быстрый интерфейс.

2. Параллельная разработка. Я объясню, в чём плюс параллельной разработки. В принципе, когда начинается разработка такого приложения, тогда веб-сервисы сначала согласовываются со спецификацией API всеми заинтересованными сторонами, описываются интерфейсы: какие сущности будут, как мы их будем извлекать, и разработка может идти параллельно. Например, и серверная часть может идти отдельно, и клиентская часть может идти вместе с серверной. Современный javascript-фреймворки позволяют работать временно с локальным хранилищем, имитируя доступ к REST API как к серверу.

3. Модульность тестирования. Качество кода использование вообще javascript-фреймворка на стороне клиента, с одной стороны, должно повысить, хотя испортить можно все. А также так как клиентская часть имеет структуру – модульную структуру – это всё можно протестировать. Если раньше мы тестировали, допустим, страницу на выходе, брали Selenium, писали сценарии и тестировали поведение самой страницы, то сейчас можно тестировать бизнес-логику, можно тестировать данные, приходящие с сервера, REST API и протестировать сам веб-интерфейс, например, unit-тестами. Можно тестировать серверную часть, уже используя серверные тестовые механизмы. Тестируемость повышается.

4. Слабая связанность компонентов – тоже плюс. У вас есть веб-сервис, который предоставляет данные по интерфейсу REST API, есть клиент. Все эти части могут изменяться, так вы можете взять и переписать сервер, например, с PHP на Java. Главное, чтобы соблюдался интерфейс взаимодействия.

Что в этой схеме неудобно?

Неудобна бизнес-логика на сервере и клиенте. Она дублируется и там, и там.

Загрузка первой страницы. Так как, скорее всего, объём JavaScript кода, включая фреймворки, будет относительно большой, то, естественно, первоначальная загрузка подразумевает какой-то временной лаг. Способов решить этот лаг существует несколько. Можно предрендерить маленький кусочек первой страницы, он загрузится быстро, и потом уже включить логику javascript-приложения.

Ну и такой немаловажный аспект, с которым мы тоже столкнулись, ища разработчиков на javascript – квалифицированных достаточно тяжело найти. Качество разработки кода на стороне фронтэнда на данный момент, как я уже говорил, судя даже по качеству кода тех приложений, которые мы встречаем, не очень высокое.

Давайте рассмотрим кратко… Наверное, REST API многие знают? Но, тем не менее, быстро пробежимся – концепция REST API универсальная. Это такой механизм работы с данными, основные его критерии: stateless, когда между запросами от клиента к серверу, состояние клиента не учитывается, то есть URL является ключом к данным: ключ-значение-кешируемость, так как REST API работает по протоколу HTTP, то и кешируемость при этом сохранится. Универсальный интерфейс – принцип работы REST API – это когда другому сервису, предлагающему доступ к данным по этому интерфейсу, вы быстро сможете обратиться, зная сущность, за которой обращаетесь.

Кратко рассмотрим основные методы доступа и вообще взаимодействия с данными. Вытащить список моделей с сервера – это GET. Если GET с указанием Id модели – вытаскивается сама модель, если POST на какой-то ресурс с данными, то создаётся запись в таблице с этими данными. Если PUT, то данные изменяются по этому Id. Если модели с этим Id на сервере нет, то она создаётся. DELETE удаляет ресурс с определённым Id.

При разработке даже спецификации API сервера следует учитывать ответы от сервера, потому что очень важно response code. Так как используется HTTP-протокол, коды важны. Перечень достаточно широк, и его нужно заранее учитывать.

Ну и формат ответа от сервера. Он может быть в формате JSON, XML, HTML, текст и как угодно. Единственное, что по умолчанию уже де-факто используется JSON – потому что браузеры могут нативно его интерпретировать в объекты, и это достаточно удобно и просто.

Переход от традиционного к SPA способу программирования: есть основные четыре пункта, по которым бизнес или разработчики как-то их остерегаются. Давайте их перечислим по порядку, и я потом поподробнее их объясню.

1. Проблема поисковой оптимизации. Поисковый робот не сможет проиндексировать одностраничные приложения, потому что, по сути, в одностраничное приложение загружены с сервера и набор шаблонов, и набор данных, которые загружаются javascript’ом. Поисковый робот не сможет обработать javascript, не сможет получить данные, не сможет проиндексировать страницу, соответственно, не сможет никаким образом её скраулить.

2. «Сломанная» кнопка «Назад». Одностраничное приложение – это одна страница, там будет что-то меняться, а как вернуться к предыдущему состоянию – непонятно.

3. Отсутствие статистики о просмотренных страницах. Как будет счётчик работать на одностраничном приложении, другими словами.
4. Ошибки на клиенте и проблемы мониторинга. Бизнес-логика, достаточно обширная и находится на клиенте. Всё сломалось – у клиента ничего не работает. Как мы отреагируем на это?

Возвращаясь к проблеме поисковой оптимизации страниц.

Так как страница у вас одна, все переходы внутри вашего одностраничного приложения будут осуществляться по ссылкам #!, дальше идут ваши параметры. То есть с точки зрения обычного браузера, это перемещения внутри страницы. Но во всех современных javascript-фреймворках есть такой модуль как роутер, который отслеживает изменения URL, и относительно того, что идёт за восклицательным знаком, зная эти параметры, он показывает соответствующее View. То есть, как и раньше, URL определяет содержание страницы. Когда на эту страницу приходит поисковый бот, то он видит ссылки исключительно с хешом. Для него это всё главная страница, по идее, он не увидит других view.

Но на самом деле поисковый бот увидев ссылку с #! заменить эти два символа на ?_escaped_fragment= и обратится по этой ссылке к вашему серверу, который в свою очередь должен быть готовым отдать чистый HTML.

Получается что страницу на серверве все-таки придется генерить?

В принципе, современные javascript-фреймворки работают с шаблонами, используя какой-либо шаблонизатор. Например, есть шаблонизатор Mustache, который работает и на серверной части в различных языках программирования PHP, Ruby, Python, и работает с javascript на клиенте. Он может использовать одни и те же шаблоны для генерации страниц – данные у вас есть, модель есть, в принципе, можно делать так.

Другой момент – можно немного схитрить и отдать поисковому боту чуть-чуть другую страницу, тем более, что ему не нужен никакой javascript, ему нужно только представление, ему нужен только CSS и HTML, нужна семантика. Может быть, в более лучшей семантике, с точки зрения SEO, вы можете ему отдать страницу.

По проблеме поисковой оптимизации я ещё хотел объяснить, как работает роутер. Так как у вас не меняется URL при кликах на ссылки внутри вашего сайта, все ссылки, как я говорил, будут на #!, после них какое-то значение, какие-то параметры. В каждом javascript-фреймворке существует модуль, называемый роутер, который отслеживает изменения URL, и отлавливает событие hashchange. Hashchange отлавливается всеми браузерами, начиная с IE8. То есть эта основа кратко описана в модуле, связанном с роутером.

«Сломанная» кнопка «Назад» – абсолютно то же самое, основано на том же самом принципе.

Так как у вас отображение на странице представления, зависит от hashchange, от URL с хешом, то, в принципе, можно взять эту ссылку, отправить своему другу, и JavaScript фреймворк сначала отработает ссылку, определит, какое View нужно показать, и отобразит его на странице.

Отсутствие статистики о просмотренных страницах: суть в том, что, так как приложение одностраничное, загрузки HTML не происходит. Обычно этот счётчик стоит где-то в футере или в начале body. Не срабатывает, не отправляется статистика на сервер. На самом деле это не так.

Гугловый пример – есть функция Push, которая, по сути, тоже вставляется в тот же самый роутер, и при изменении хеша он может отправлять данные, как минимум, о хеше. Вы можете добавлять ещё какие-то свои параметры, что ещё более удобно, и анализировать более подробно само поведение пользователей на странице.

Ошибки на клиенте, о которых мы никогда, вроде как, не узнаем.

Возможны ошибки, так как кода без ошибок не бывает, как бы ни тестировали. Что делать? Как вариант – отлавливать эксепшены на самом-самом верхнем уровне, и отправлять их Ajax на сервер, и там их логировать. Точно так же, как и раньше, сигнализировать в службу поддержки, чтобы они могли отреагировать.

Какой JavaScript фреймворк выбрать? Очень сложный вопрос, и на него нет никакого чёткого ответа. Все фреймворки очень разные, все пытаются решить какие-либо задачи, вопросы.

Единственное, что могу сказать – нужно смотреть, выбирать, что ближе, какие задачи вам надо решать. Единственный совет – если вы новичок, если вы впервые планируете заниматься javascript-фронтэнд программированием, то лучше не брать такие простые, абстрактные библиотеки, как Backbone.js, например, потому что лучше, чтобы пожёстче была структура, была документация подробная, чтобы можно было не уйти в сторону и не написать опять бесполезный код. Мы, например, используем Marionette и Backbone.js.

Вот такая вот была лекция. В принципе, цель её была объяснить, что если вам нужен расширенный доступ к данным, быстрый интерфейс, то можно использовать REST API и «толстый» клиент (то, что мы называем – SPA). Не бойтесь, всё это решаемо. Вообще, развитие браузеров идёт вперёд, и в дальнейшем даже то, что мы видим, я думаю, будет невидимым.

Спасибо, если есть какие-то вопросы…(аплодисменты)</span class=”italic”>.

 


Вопросы из зала:

 

– Спасибо за доклад. У меня вопрос такого плана. REST API – это очень хорошо и правильно. Не испытываете ли вы каких-то проблем в том, что использование REST API не может показать, что будет в итоге. Не бывало ли таких случаев, когда RPC было бы более практично?

Андрей Лебедев: RPC – наверное, такие случаи бывают, но, с точки зрения одностраничного приложения совсем непонятно, как использовать RPC. И удобней, как мне кажется, использовать REST API. И для клиентских приложений – я имею в виду мобильные.

 

– Просто REST API – по сути это модель в открытом виде.

Андрей Лебедев: Да. Но ее можно и нормализовать, соответственно, до того уровня, который вам необходим. Не так уж прямо, чтобы строчку в таблице целиком отдаёте, а нормализованно.

 

– То есть нормализованный отличается тем, что это идёт REST API, но он идёт не один к одному в модели.

Андрей Лебедев: Нет, абсолютно. Вы как раз можете использовать не реляционную базу данных, а, например, MongoDB, и у вас будет уже структура самого ответа другая, более расширенная. Вы можете не Id вставлять, а сразу какие-то значения.

 

– Здравствуйте, у меня вопрос по процедуре тестирования.

Андрей Лебедев: На данный момент – мы относительно недавно занимаемся такими разработками – и на данный момент мы тестируем Selenium сам интерфейс, как раньше мы тестировали, и тестируем JUnit интерфейс снаружи.

 

– Спасибо за доклад, у меня вопрос про кеширование. Вы упомянули по поводу HTTP REST API, что там есть в протоколе HTTP кеширование. И вот как вы кешируете?

Андрей Лебедев: Мы это не используем. Мы кешируем информацию в кэше, она у нас на сервере, и, естественно, иногда используем кеширование на клиенте (LocalStorage). Именно кеширование запросов мы не используем. Но оно возможно.

 

– Здравствуйте. У меня вопрос: как вы осуществляете синхронизацию бизнес-логики на клиенте и бэкэнде? С точки зрения бизнеса, то есть у вас стоит задача сделать логику. Как вы приходите к тому, что это логику надо сделать на бекэнде, а эту на фронтэнде?

Андрей Лебедев: Мы её сначала очень подробно описываем в спецификации.

 

– Есть ли какой-то инструмент для генерации чего-то, или это чистый JS отдельно, и чистый бэкэнд отдельно?

Андрей Лебедев: Абсолютно точно.

 

– Подскажите, пожалуйста. Вчера высказывалась такая мысль, что проблема первоначальной загрузки страницы вполне решаема тем, что изначально мы получаем данные в HTML. Мы используем REST API, но когда мы запрашиваем HTML, мы получаем HTML отрендеренный.

Андрей Лебедев: Да, я и говорил, что используется рендеринг – сначала получаете маленький кусочек страницы, чтобы она сразу пользователю отобразилась, потом включаете фреймворк, который подгружает остальные данные.

 

– То есть это уже используется?

Андрей Лебедев: Конечно, почему нет. Это и Фейсбук использует, и Гугл.

 

– Здравствуйте. Мне кажется, одной из проблем этого подхода – вы её, в принципе отметили в докладе – является то, что трудно найти толковых javascript разработчиков. Как у вас организована работа на проектах фронтэнд-разработчиков?

Андрей Лебедев: Фронтэнд-разработчики перемещаются между проектами, потому что полностью загрузить большое количество фронтэнд разработчиков невозможно.

 

– Я немного расширю вопрос про бизнес-логику на фронтэнде и бекэнде. У нас есть какая-то бизнес-логика, которую делает JavaScript, и в тех случаях, когда надо отдавать для поисковиков HTML – соответственно, на бекэнде тоже будет какая-то логика. Не пытались ли вы этот код как-то совместить с помощью javascript или чего-то подобного?

Андрей Лебедев: Это хороший подход, просто мы в основном – так исторически сложилось – используем в нашей компании Grails. Javascript был бы хорошим решением в данном вопросе. Но всё только начинается, возможно, мы будем его использовать.

 

– Подскажите, пожалуйста, ещё. Вы говорили про тестирование бекэнда с помощью JUnit. Хотелось бы уточнить состояние приложения, при котором оно тестируется. Оно задаётся тоже через какие-то методы API, либо мы через само приложение его формируем?

Андрей Лебедев: Мы пишем модуль, покрываем его тестами, снаружи создаём через Rest API.

 

– А состояние базы? Само приложение получается – есть, например, какой-то товар, обращаемся к методу API, и вот мы его получаем.

Андрей Лебедев: Да. Создаём, получаем.

 

– И как вы его создаёте? Через свою модель приложения?

Андрей Лебедев: POST, да.

 

– А, POST, то есть специальный самого API?

Андрей Лебедев: Да.

 

– History API планируете в будущем использовать?

Андрей Лебедев: Конечно.

 

Ведущий: Может, у кого-то ещё вопросы есть? Тогда поблагодарим докладчика.
Аплодисменты.</span class=”italic”>