«Совершенный Ajax» — новый подход к построению web-приложений, при котором web-сервер не генерирует ни строчки HTML-кода и взаимодействует с внешним миром только посредством web-служб; а клиентский интерфейс реализуется только на основе клиентских HTML, CSS, JavaScript.
Статья состоит из двух частей. В первой части — более живой и провокационной я постараюсь заинтересовать проблемой, рассказать о технологии «Совершенный Ajax» и показать её применение на примере нашего проекта «Система Интерактивного Тестирования Знаний „Синтез“» (который имеет ряд интересных особенностей, таких, как использование серверного JavaScript на платформе Mozilla Rhino, прототипно-ориентированная ORM и поддержка SPARQL — языка запросов к Semantic Web).
Вторая часть – более занудная будет содержать много технических деталей и выйдет в следующий раз.
Попробуйте угадать: к какой архитектуре относятся web-приложения?
К клиент-серверной говорите? Я ожидал, что Вы так ответите…
Что ж, давайте разберёмся. В клиент-серверной архитектуре выделяют [1]:
Сервер — отвечает за хранение данных и бизнес-логику приложения.
Клиент — отвечает за взаимодействие с пользователем.
Реализация бизнес-логики на сервере и взаимодействие с пользователем на клиенте чётко разделены.
Преимущества клиент-серверной архитектуры очевидны; мы их все знаем:
Бизнес-логика не смешивается с пользовательским интерфейсом.
Можно реализовать несколько клиентов с разными пользовательскими интерфейсами: интерфейс командной строки, оконный Windows-интерфейс, Flash, web-интерфейс, мобильный интерфейс и т.д.
Клиентский компьютер не требователен к ресурсам;
И т.д.
Но, относятся ли web-приложения к клиент-серверной архитектуре?
Действительно, в web-приложениях есть сервер, отвечающий за бизнес логику приложения.
Но! За реализацию интерфейса отвечает не клиент, а тоже сервер. На сервере происходит обработка клиентской формы. Сервер генерирует HTML-код пользовательского интерфейса.
Клиент, т.е. браузер лишь визуализирует уже готовый HTML-код интерфейса.
Это, фактически, то же самое, что прицепить к серверу монитор и объявить этот монитор клиентом…
Здесь, правда, есть одна тонкость. Следует различать два понятия: web-приложения и систему «браузер — web-сервер». Web-приложения работают поверх браузера и web-сервера, так же как Java-приложения работают поверх JVM, приложения на .Net работают поверх .Net Framework, а протокол HTTP работает поверх TCP/IP.
Система «браузер — web-сервер» действительно имеет клиент-серверную архитектуру: web-сервер принимает и обрабатывает запросы, а браузер визуализирует результат.
Однако, здесь мы говорим не о системе «браузер — web-сервер», а о работающих поверх неё web-приложениях.
Вряд ли такой подход можно назвать полноценной клиент-серверной архитектурой. Он имеет много недостатков:
Смешивание бизнес-логики и пользовательского интерфейса;
Сложно реализовать несколько пользовательских интерфейсов;
Сторонни программы не могут обращаться к серверу (если не написан специальный api);
Большая часть нагрузки по обработке интерфейса ложится на сервер.
И т.д.
Впрочем, мы знаем в истории пример подобной архитектуры.
В 70-годы были распространены мейнфреймы.
Мейнфрейм — такой огромный железный сундук (сервер), к которому подключались рабочие станции (клиенты).
Причём, рабочая станция представляла собой просто монитор с клавиатурой.
И любые действия клиента на рабочей станции обрабатывалось на сервере,
порой даже такие как обработка нажатия на клавишу и обрисовка экрана
[2].
Ну, мы знаем, насколько популярны мейнфреймы сегодня…
Конечно, в современных web-приложениях часть интерфейсной логики реализуется на клиенте с помощью JavaScript.
Часть данных загружается с помощью Ajax и визуализируется именно на клиенте.
Но, тем не менее, многие действия связанные с пользовательским интерфейсом, по-прежнему, выполняются на сервере. Использование Ajax сейчас во многом даже усугубляет ситуацию, т.к. приводит к разбрасыванию реализации интерфейса между серверным и клиентским кодом.
Так вот, я предлагаю подход «Совершенный Ajax», который призывает развить идею Ajax до логического конца и полностью отказаться от использования сервера для реализации пользовательского интерфейса web-приложений.
Подход «Совершенный Ajax» построен на следующих принципах:
Реализует только бизнес-логику приложения и не генерирует ни строчки HTML-кода;
Взаимодействует с клиентом посредством web-служб: принимает текстовые запросы и возвращает только данные;
Реализуется только на основе клиентских HTML, CSS, JavaScript.
Взаимодействует с web-сервером посредством объектно-ориентированной библиотеки-обёртки над web службами;
Используется исключительно семантическая вёрстка.
Элементы управления (вкладки, меню, деревья и т.д.) описываются высокоуровневыми HTML-конструкциями.
Библиотека контролов придаёт HTML-конструкциям внешний вид и функциональность соответствующего элемента управления, просто навешивая нужные стили и обработчики событий, не меняя при этом HTML-код элемента.
Любой сторонний разработчик может реализовать свою версию пользовательского интерфейса, причём не только на HTML, но и на Flash, Windows, Mac и т.д.
Опишу эту архитектуру на примере нашего проекта «Система Интерактивного Тестирования Знаний „Синтез“».
В концепции «Севершенный Ajax» сервер должен удовлетворять одному-единственному условию: не генерировать ни строчки HTML-кода и осуществлять связь с внешним миром посредством web-служб. Во всём остальном его реализация ничем не ограничена.
Здесь я опишу структуру сервера в нашем проекте, т.к. она имеет ряд интересных особенностей: использование серверного JavaScript на платформе Mozilla Rhino, прототипно-ориентированная ORM и возможность использования SPARQL — языка запросов к Semantic Web.
Однако, Ваша реализация сервера может быть совершенно иной и совсем не похожей на нашу.
СУБД — хранит данные.
Прототипное объектно-ориентированное ядро бизнес-логики — реализует объектную модель и функциональность приложения.
ORM — связывает базу данных с прототипным ядром бизнес логики.
Web-сервер — открывает функции программы для внешнего мира посредством web-служб.
Все данные хранятся в обычной реляционной базе данных.
Причём, используется только «чистый» SQL, без надстроек конкретных СУБД: хранимых процедур и т.д. Это делает базу данных независимой от той или иной СУБД.
Связь базы данных с объектным ядром бизнес-логики приложения осуществляется через прототипно-ориентированную ORM.
Вся объектная модель приложения и его бизнес-логика заключена в прототипно-ориентированном ядре.
Ядро написано на серверном JavaScript, на базе платформы Mozilla Rhino.
Несмотря на недооцененность многими разработчиками, JavaScript — удивительно мощный, гибкий и красивый язык, превосходящий в ряде случаев по гибкости и функциональным возможностям таких монстров, как Java или C#. И мы используем по максимуму его возможности, такие как прототипно-ориентированное ООП, объекты-как-хеши, функциональное программирование, замыкания и т.д.
Также, прототипно-ориентированная парадигма JavaScript позволяет гораздо более гибко работать с базой данных через ORM.
Mozilla Rhino компилирует JavaScript в байт-код JVM.
Благодаря этому, наш серверный JavaScript не становится «вещью в себе», а может использовать всё обилие наработок мира Java.
Вообще, серверный JavaScript на основе Mozilla Rhino — это отдельная большая тема, и я постараюсь написать ряд статей по этому вопросу.
ORM служит для связи реляционной БД с прототипным объектно-ориентированным ядром бизнес-логики.
Она позволяет не просто связывать две модели, но и строить объектные запросы любой сложности.
Например:
найди классы, ученики которых по тестам 10 самых молодых преподавателей
имеют за осень средний балл больший, чем по весенним тестам 10 самых старших.
Использование ORM для прототипно-ориентированных языков — это мега круто!
Реляционная модель БД гораздо ближе к прототипной модели ООП, нежели к классовой. Поэтому, применение прототипного похода решает ряд проблем, присущих современным класс-ориентированным ORM (см., например, статью Теда Ньюарда «Вьетнам компьютерной науки»).
Прототипная парадигма ORM позволяет удивительно гибко работать с данными.
Ведь, в отличие от классового подхода, где структура объекта жёстко задана; при прототипном подходе объект может иметь произвольный набор полей, а также объединять внутри себя и наследовать любые другие объекты.
В частности, это даёт возможность обращаться к БД на SPARQL — языке запросов Semantic Web.
К сожалению, за такую гибкость приходится платить производительностью. Наша прототипная ORM пока существенно уступает по скорости работы обычным классовым ORM. Но мы работаем над этим.
Прототипно-ориентированная ORM — также отдельная большая тема, и я постараюсь посвятить ей ряд статей.
Web-сервер выполняет только одну-единственную задачу — связывает ядро бизнес-логики с внешним миром посредством web-служб.
При этом он не генерирует ни строчки HTML-кода.
Web-службы не просто открывают доступ к ограниченному предопределённому набору функций, а полностью реализуют все возможности приложения.
Например, при наличии соответствующих прав, через web-службы можно осуществить объектный запрос неограниченной сложности и получить произвольную выборку объектов.
Наше приложение может иметь сколько угодно интерфейсных реализаций, разработанных любыми производителями.
Однако, оно всегда имеет в комплекте «родной» web-интерфейс.
Здесь я опишу его структуру.
Интерфейсное ядро — объектно-ориентированная библиотека, реализующая всю клиентскую логику и управляющая интерфейсом клиента.
Библиотека-обёртка — объектно-ориентированная обёртка над web-службами, осуществляющая связь клиентского интерфейса с сервером.
Семантическая вёрстка — используется для описания элементов интерфейса (контролов) посредством обычных HTML-конструкций;
Библиотека контролов — придаёт HTML-конструкциям внешний вид и функциональность соответствующего контрола.
Интерфейсное ядро — объектно-ориентированная JavaScript-библиотека, управляющая всем клиентским web-интерфейсом:
Реализует интерфейсную логику приложения.
Взаимодействует с объектной моделью приложения на web-сервере через библиотеку-обёртку.
Взаимодействует с элементами управления (контролами) посредством библиотеки контролов.
Осуществляет встраивание клиентского интерфейса нашего программы в web-интерфейсы других приложений.
Это даёт неограниченные возможности для создания мэшапов. В отличие от обычных мэшап-приложений, таких как GMaps, YouTube и т.д., которые позволяют встраивать только небольшую часть интефейса; в нашем приложении интерфейс может встраиваться полностью.
Например, благодаря этому, на стороннем сайте можно не только разместить модуль прохождения тестирования, но и модуль редактирования тестов, а также управления правами пользователей.
Управляет стилями интерфейса.
Стили не только позволяют задавать оформление программы, но и полностью контролируют расположение и свойства интерфейсных элементов. Особенно это полезно, когда программа работает не самостоятельно, а, встроенна в другое приложение в виде мешапа.
Управляет локализацией интерфейса.
Клиентская реализация (как родной web-интрфейс, так и интерфейсы сторонних производителей) взаимодействует с объектной моделью и бизнес-логикой на сервере посредством web-служб.
Однако, работать с web-службами напрямую неудобно: это лишает нас объектного подхода и понижает упровень абстракции.
Поэтому, гораздо удобнее работать через объектно-ориентированную библиотеку-обёртку над web-службами. Библиотека обёртка позволяет прозрачно работать с серверной объектной моделью приложения.
Нам надо получить объект Морковкин Вася и сделать его учеником 3 «А» класса.
Вместо низкоуровневой работы с web-службами, мы прозрачно работаем
с объектной моделью приложения посредством библиотеки-обёртки Sintez.
//Получаем с сервера объект Морковкин Вася
var objStudent1 = Sintez.getStudent ("this.firstName = 'Вася' and this.secondName = 'Морковкин'");
//Получаем объект 3 "А" класс
var objClass1 = Sintez.getClass ("this.getClassNuber() = 3 and this.liter = 'А'");
//Делаем Васю учеником этого класса
objClass1.addStudent (objStudent1);
А уже библиотека-обёртка кодирует вызов методов объектов как команды web-служб, пакует объекты перед отправкой их на сервер и распаковывает после получения.
Для того, чтобы свою версию интерфейса мог реализовать любой сторонний производитель на любой платформе, мы выпускаем библиотеки-обёртки не только для web-интерфейса на JavaScript, но и для других распространённых технологий: .Net, Java, Delphi, Flash и др.
Если для какой-то платформы мы ещё не выпустили библиотеку-обёртку, сторонний разработчик может её реализовать и самостоятельно по заданной спецификации.
Кстати, если серверный язык программирования поддерживает интроспексию, библиотека обёртка может генерироваться автоматически.
Для этого, программа на сервере должна исследовать собственную объектную модель, и на её основе сгенерировать код библиотеки-обёртки для основных языков программирования.
Пока мы пишем библиотеки-обёртки вручную, но, думаю, скоро доберёмся и до автоматической генерации.
Весь пользовательский интерфейс реализуется на основе чистых xHTML, CSS и JS.
Несмотря на сильную недооцененность разработчиками, xHTML/CSS/JS является очень мощной и гибкой технологией построения интерфейсов.
Использование семантической вёрстки является высокоуровневым программированием; а несемантическая мешанина тегов – низкоуровневым.
И если при старом подходе, когда HTML-код генерировался сервером, мы, несмотря на всё уродство, могли себе позволить низкоуровневый несемантический подход;
то при подходе «Совершенный Ajax» мы просто обязаны использовать высокоуровневую семантическую вёрстку!
Элементы управления (деревья, меню, вкладки и т.д.) описываются обычными высокоуровневыми HTML-конструкциями.
Например, меню или дерево описывается как обычный список, а вкладки, как набор div’ов.
А уже библиотека контролов делает из этих HTML конструкций элементы управления.
Библиотека контролов придаёт HTML-конструкциям внешний вид и функциональность соответствующего элемента управления.
При этом HTML-код элемента не меняется, а на него просто навешивая нужные стили и обработчики событий.
Для превращения HTML-конструкции в элемент управления, надо просто вызвать соответствующий JS-объект библиотеки контролов.
Работа с контролом происходит через вызов методов объекта.
Поскольку контролы не меняют код своей HTML-конструкции, элемент управления может быть «на лету» превращён в другой элемент простой заменой JS и CSS-классов.
Дерево описывается не мешаниной тегов, а единой высокоуровневой HTML-кострукцией: вложенным списком.
<li>
Элемент 1
<ul>
<li>
Элемент 1-1
</li>
<li>
Элемент 1-2
</li>
</ul>
</li>
<li>
<!--...-->
</li>
</ul>
!!!]]>
Для придания списку внешего вида и функциональности дерева, создаётся JS-объект Controls.Tree.
Работа с деревом происходит через вызовы методов объекта.
//Создаём дерево
var objTree1 = new Controls.Tree ($("ulTree1"));
//Выделяем все узлы дерева, вызывая метод selectAll () созданного объекта
objTree1.selectAll ();
В любой момент мы можем превратить дерево в меню, просто создав объект Controls.Menu:
//Превращаем дерево в меню
var objMenu1 = new Controls.Menu ($("ulTree1"));
В скором времени я собираюсь опубликовать статью на эту тему: «HTML — самый недооценённый язык построения интерфейсов в мире (СНЯПИМ)».
Подробности технической реализации подхода «Совершенный Ajax» — во второй части.
↑ Речь идёт о клиент-серверных приложениях с конечным пользователем. В клиент-серверных приложениях вроде «клиент — сервер базы данных», пользовательский интерфейс, разумеется, отсутствует.
↑ Позже, у мейнфреймов появились так называемые «умные клиенты», которые обладали собственным процессором и памятью, а наиболее продвинутые могли даже проверить форму перед отправкой на сервер. Это очень напоминает нынешнюю робкую попытку передать часть интерфейсной логики web-приложения на клиент с помощью Ajax.
«Совершенный Ajax». Часть 2 (скоро будет)
4 подхода к построению клиент-серверных web-приложений (скоро будет)
HTML — самый недооценённый язык построения интерфейсов в мире (СНЯПИМ) (скоро будет)
Серверный JavaScript на платформе Mozilla Rhino (пока не готово)
Прототипно-ориентированная ORM (пока не готово)
Попробую резюмировать :)
Делаем передачу данных в формате XML или JSON (или каком-нибудь еще) и получаем "Совершенный AJAX".
Не понятно о какой библиотеке контролов идет речь. Вашей собственной (вы ее продаете или свободно распространяете?) или существующей?
@Владимир
Делаем передачу данных в формате XML или JSON (или каком-нибудь еще) и получаем "Совершенный AJAX".
Главное в подходе «Совершенный AJAX» то, что:
А XML или JSON — это лишь средство для работы web-служб.
Не понятно о какой библиотеке контролов идет речь. Вашей собственной (вы ее продаете или свободно распространяете?) или существующей?
При подходе «Совершенный AJAX» можно использовать любую клиентскую библиотеку контролов — как самописную, так и уже готовую: ExtJS, Dojo, jQuery UI и т.д.
Главное, чтобы превращение HTML-элемента в соответвующий контрол, по возможности, происходило только путем навешивания стилей и обработчиков событий, без изменения его кода (DOM структуры).
В нашем проекте мы, по некоторым причинам, используем собственную внутреннюю библиотеку; однако, в принципе, при подходе «Совершенный AJAX» можно использовать библиотеки любых производителей.
>> web-сервер не генерирует ни строчки HTML-кода
Допустим, серверный передает дату. Он может сделать несколькими способами:
1) как метку времени;
2) отформатированную функцией date (или аналогичной).
Формально ни в том, ни в другом случае HTML разметки нет.
Но очевидно, что во втором случае разработчику клиентского интерфейса работать будет сложнее, особенно если при форматировании будет отброшена часть информации (например, секунды).
Поэтому, на мой взгляд, требование "web-сервер не генерирует ни строчки HTML-кода" нужно изменить на "web-сервер не форматирует данные вообще".
Идея понравилась, звучит очень заманчиво, но у такой схемы есть и минусы:
- большАя часть нагрузки перекладывается на клиентскую машину. Уже сейчас есть сайты, использующие AJAX, при просмотре которых мой комп начинает тормозить. А уж если в JS будет какая-нибудь ошибка, приводящая к утечкам памяти...
- стандартный интерфейс становится недоступным для альтернативных клиентов, типа мобильных или текстовых браузеров (можно под них написать свою клиентскую часть, но кто этим будет заниматься?).
- если при обычном "несовершенном" AJAX поисковики проиндексируют на сайте хоть что-то, то тут, подозреваю, с индексацией будет совсем плохо.
Так что этот принцип подходит далеко не для всех проектов.
Кстати, а что вы используете для обмена данных между JS и сервером: JSON или XML (просто статистику собираю)?
@Глеб Белогорцев
Так что этот принцип подходит далеко не для всех проектов.
Совершенно правильно. Для сайтов, разумеется, следует использовать традиционный подход.
Архитектура «Совершенный Ajax» предназначена для web-приложений (программ).
Это и решает многие приведенные Вами проблемы:
с индексацией будет совсем плохо
web-приложениям (таким, как GMail, GMaps и т.д.) не требуется индексация в поисковиках.
стандартный интерфейс становится недоступным для альтернативных клиентов, типа мобильных или текстовых браузеров
Вообще, обычные web-приложения итак далеко не всегда доступны для альтернативных клиентов.
При подходе «Совершенный Ajax», сделать интерфейс доступным для альтернативных клиентов, становится даже проще, чем в обычных приложениях.
Поскольку элементы управления (вкладки, меню, деревья) получаются из соответствующих HTML-конструкций простым навешиванием CSS и JS-классов, их очень просто адаптировать для альтернативных устройств.
Например, если у нас есть контекстное меню (требующее правую кнопку мыши и не работающее на мобильных устройствах), его можно превратить в понятный для мобильника многоуровневый список, просто заменив соответствующие CSS и JS-классы.
От клиента требуется только поддержка JavaScript.
В самом тяжелом случае, кода клиент не поддерживает даже обычный JavaScript, можно реализовать альтернативный облегченный интерфейс. Поскольку при подходе «Совершенный Ajax» бизнес-логика полностью отделена от интерфейса, написать альтернативный клиент гораздо проще, чем при традиционном подходе.
Ждем продолжения!надеюсь будет!
Маленький технический вопрос:
//Получаем с сервера объект Морковкин Вася
var objStudent1 = Sintez.getStudent ("this.firstName = `Вася` and this.secondName = `Морковкин`");
//Получаем объект 3 "А" класс
var objClass1 = Sintez.getClass ("this.getClassNuber() = 3 and this.liter = `А`");
//Делаем Васю учеником этого класса
objClass1.addStudent (objStudent1);
Посмею предположить, что в данном коде выполняется как минимум три синхронных AJAX-запроса. я прав?
@MX
Посмею предположить, что в данном коде выполняется как минимум три синхронных AJAX-запроса. я прав?
Да, действительно, в приведенном примере генерируются три запроса (по одному на каждую операцию).
Понятное дело, что при написании сложных функций, запросы, если это возможно, сначала помещаются в буфер, а затем скопом отсылаются на сервер.
//Получаем с сервера объект Морковкин Вася
var objStudent1 = Sintez.getStudent ("this.firstName = `Вася` and this.secondName = `Морковкин`");
//Получаем объект 3 "А" класс
var objClass1 = Sintez.getClass ("this.getClassNuber() = 3 and this.liter = `А`");
//Делаем Васю учеником этого класса
objClass1.addStudent (objStudent1);
Это работать не будет. Идёт асинхронный запрос, код продолжает выполняться, и ответ придёт позже. может быть у xmlhttp и есть какой-то флаг, позволяющий исполняться синхронно(и блокировать джаваскрипт то ответа), но тогда выходят жуткие тормоза в системе(особенно если сюда цикл воткнуть).
Получается, что надо на сервер передавать описание действий, а сервер их должен выполнить и отдать ответ.
Но никак не сделать вот такие три запроса, бизнес логика придумана совсем не для клиента, а только для сервера
Это хорошая идея по поводу совершенного AJAX, но все равно предварительно потребуется каким-то образом иметь у пользователя все необходимые скрипты и непосредственно странички.
Тема интересная, без сомнения...
И у нее есть будущее, так как, именно в этом направлении рынок и собирается развиваться!
Помнится для одной практической работы - " ... "
На клиент изначально засылалась оболочка, а в виде "транспортного протокола общения" был выбран XML...
А плюсы всего этого:
+ Логика отделена от данных
+ Возможность смены дизайна
+ И самый главный не нужно каждый раз генерировать на сервере "визуальную" составляющую...
+ ...
+ ...
Но есть и один главный минус, чтобы пользоваться всем этим добром нужно сначала доставить оболочку клиенту...
//Получаем с сервера объект Морковкин Вася var objStudent1 = Sintez.getStudent ("this.firstName = `Вася` and this.secondName = `Морковкин`"); //Получаем объект 3 "А" класс var objClass1 = Sintez.getClass ("this.getClassNuber () = 3 and this.liter = `А`"); //Делаем Васю учеником этого класса objClass1.addStudent (objStudent1);
Алик, я солидарен с ZeusTheTrueGod
Хотелось бы услышать ваш комментарий по поводу необходимости отложенной обработки таких асинхронных вызовов.
Т.е. строчку objClass1.addStudent (objStudent1) имеем право выполнять только после того, как убедимся, что на первые два вызова сервер уже ответил.
@ZeusTheTrueGod, @Сергей Титенко
откуда Вы взяли, что XHR выполняется асинхронно ???
var data = getData(); // XMLHttpRequest
alert ("data is " + data);
такая конструкция работает, хотя если бы она была асинхронной - то алерт всегда давал бы <null>, "" или "undefined".
Тема интересная.
Но Ajax несомненно нуждается в доработке. Многие нюансы ведут к его некорректной работе.
я вот уже полгода активно учу аякс, но с каждым днем узнаю что-то новое