Isomorphic javascript
Mar. 10th, 2015 01:19 am![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Новое модное веяние, во многом – просто красивое название для концепции distributed render, которая у меня оформилась в середине 2012. Дополнительно к distributed render это ещё и быстрая синхронизация локальных данных клиента и сервера, максимальная согласованность того, что в UI и того, что в главной БД.
Ну и главная фишка – более-менее единый код для клиента и для сервера.
Я с этими штуками за 3 последних года вдоволь наэкспериментировался вдоль и поперёк – просто не называл это isomorphic javascript. В этой связи у меня оформилось несколько стойких соображений.
Изоморфность, когерентность и диффы
Изоморфные веб-приложения – это, конечно, такая серебряная пуля. В идеале – отображение удалённой БД на UI, работающее в обе стороны в реальном времени.
Самая главная беда у нас по пути – канал связи. Мы заранее не знаем какой он у нас будет – и это главное соображение, почему серебряной пули не случится. Когда приложение исполняется локально, скорость отображения изменений UI на подлежащий документ известна и она очень высока.
Когда мы отображаем UI на remote документ, мы рискуем получить либо невероятные задержки в UI, либо ссылочную некогерентность данных UI и сервера.
Простой пример. У нас есть чатик, который дописывает узлы в какой-то док по мере появления реплик. Пусть он их передаёт diff-ами, например.
Мы начинаем передавать в чатик 10 картинок по 10 мегабайт каждая. Добавляем их в UI – и хотим написать реплику со ссылкой на одну из картинок.
Если мы требуем ссылочной когерентности – реплика не сможет быть отправлена в БД до того, как туда попадут картинки. Это, конечно, так себе чатик будет.
То-есть требование ссылочной когерентности отвалилось на простейшем юз-кейсе. Ну или нам надо сделать такую БД в которой два произвольных апдейта всегда коммутируют – что-то не видать таких, ога.
Итого: изоморфность – наверное, когерентность – видимо нет.
Декомпозиция задачи
В самом деле, ссылочная когерентность UI и remote-отображения в подавляющем большинстве случае не необходима. Если не выдвигать её как требование, задача распадается на четыре части.
Мы, в идеале, хотим иметь такую среду исполнения, приложения которой:
- можно использовать на клиенте как UI для модификации подлежащего документа,
- можно использовать на сервере как валидатор,
- можно использовать на сервере как параметризуемый шаблон, генерящий HTML,
- не должны заботиться, где вообще находится документ и его реплики.
Ну и мы подразумеваем, что у нас приложение – это одинаковая сущность и для клиента, и для сервера.
Условие №1, клиентский UI – чисто здравый смысл. Приложения – они для людей.
Условие №2, валидатор из приложения – самое главное. Валидация приложением – это когда мы можем взять документ, взять код приложения и не выполняя его в части UI определить, мог ли документ быть получен нормальной работой этого аппа в соответствующем окружении.
Для того, чтобы мы могли это сделать для начала в теории, наше приложение должно образовывать индуктивный тип, такую хитрую математическую конструкцию.
С точки зрения математики каждый валидный документ приложения образует неподвижную точку для функции, которая “вычисляет” приложение над документом.
С точки зрения пользователя это эквивалент операций Открыть >> Сохранить, после которого документ не должен измениться, и по пути не должно возникнуть никаких ошибок. Вот только всё это надо иметь возможность делать без UI.
Я совершенно определённо знаю, что это возможно. Такой путь точно не грозит приложениям angular, а вот react вплотную к этому подошёл. Имхо react-комьюнити всё не может решиться осознать, что не надо никакой отдельной схемы, приложение само может быть схемой, если немного унифицировать его структуру.
Условие №3. Приложение как параметризуемый шаблон. Это считается главной серебряной пулей, чуть не самой желанной фичей. Дескать чтобы вот передать на сервер URL – а он тебе HTML-снапшот приложения в нужном состоянии.
Или не передавать – а попросить с сервера данные и отрендерить приложение на клиенте, но чтобы то же самое получилось.
Это серебряной пулей считается напрасно, хотя усилия по реализации предприняты в целом колоссальные. Это примерно так же переоценено, как responsive layouts. Да, иногда нужно, и даже удобно – но если всё так делать, то это очень ограничивает.
Я вот тут развиваю мысль, почему это в общем случае делать незачем. Главная мысль – потребитель всех этих усилий в итоге гугл, а не люди. А гуглу нужны данные и ссылки, а не состояния вашего приложения.
То-есть не надо смешивать две задачи:
- Максимально эффективно скормить гуглу вашу базы.
- Иметь на сервере функцию, которая может вам прислать HTML-снапшот вашего приложения в любом его состоянии.
Первая задача стОит, чтобы её решать, вторая – ну её нафиг.
Условие №4. Среда исполнения должна сама выбирать стратегию распространения изменений по уровням кэша (локальная БД >> удалённая БД или несколько удалённых БД, пиринговая доставка другим юзерам, дайджесты на емэйлы и тп).
Ну или как минимум давать максимально удобно конфигурировать параметры стратегии.
Для приложения все эти детали должны быть скрыты, у него одна операция – сэйв, и один ивент – внешний апдейт документа.
Я такую конструкцию в части API к БД смог довольно быстро собрать – но в целом оно довольно неудобно. С одной напрягает бардак с promise. С другой – с памятью.
Клиентские библиотеки и API не обязаны быть организованы так, чтобы память экономить. RAM сейчас для большинства клиентских приложений ресурс почти не ограниченный.
Серверные API, напротив, просто обязаны давать возможность обрабатывать данные экономно с точки зрения монопольного занятия CPU и памяти надолго.
Это, вообще говоря, требования противоречивые (ситуация кста касается и пункта №2 про валидатор).
То-есть если мы хотим, чтобы наше изоморфное приложение загрузило при старте пяток мегабайт данных для дропдауна (пусть виртуального) через какое-то унифицированное и для клиента, и для сервера API, нам понадобится очень жирный сервер.
Потому что эти 5 мегов придётся загружать в каждую серверную сессию при валидации данных этого приложения, пришедших удалённо. Можно какие угодно предпринимать усилия по дедупликации, и всё равно найдётся такая задача, где стратегия дедупликации поломается и сервер начнёт жрать память и размножать данные по сессиям.
Итого
Изоморфные в полный рост приложения – это довольно нишевый продукт, потому что сейчас делать и владеть ими дорого, поддерживать сложно и все имеющиеся решения очень громоздки.
Что-то простое из них можно собрать просто и коротко, а вот сложное – получается непременно хромота с какой-нибудь стороны.
Конечно, кто-то непременно решит всю задачу целиком и красиво, но это всё равно не будет серебряной пулей – есть определённые области, где подход точно не применим.
Вообще, думаю, тут не обойдётся без появления нового языка (или следующей итерации JS) – и он будет ближе к Erlang по компоновке кода и соглашениям, чем к JS.
no subject
Date: 2015-03-10 01:50 am (UTC)Не очень "сильных", но FYI.
1. Твой пример с чатиком плох. Ты вполне можешь знать ссылку до того, как картинки докачаются. Клиент может уметь генерировать новые глобальные идентификаторы сам, или может попросить сервер выдать ему такой, ещё до окончания закачки.
2. Возможность генерировать HTML на сервере важна для того, чтобы делать gradual service degradation вместо 100% failure. Например, сервис вроде ЖЖ может сгенерировать статичные HTML для популярных страниц с кучей комментов, выложить их на отдельный хостинг и во время ДОС-атаки делать очень дешёвый редирект, или просто выдачу отрендеренной страницы из кэша вместо дорогих запросов в базу, чтобы зачитать содержимое поста и комментов.
Ещё (предположу, что) она может быть важна с архитектурной точки зрения: ты таким образом уменьшаешь количество потенциальных багов, когда для какого-то состояния на клиенте нет корректной серверной репрезентации, и, как следствие, будут всякие неприятные сайд-эффекты, невозможность зарефрешить страницу без потерь и всё такое прочее. В идеале пара {URL, динамическое состояние пользователя на сервере} должны полностью определять страницу, чтобы её можно было перерисовать по рефрешу, или если пользователь пересядет в другой обозреватель и т.п.
3. В общем случае для поисковиков нужно писать отдельный рендеринг engine (и он может быть куда проще, потому что тебе не нужна динамика), что полностью снимает любую аргументацию в пользу оптимизации UI-фреймворка под какие-то потребности поисковиков.
no subject
Date: 2015-03-10 02:12 am (UTC)Пример кстати не гипотетический, я вживую этого наелся.
no subject
Date: 2015-03-10 04:31 am (UTC)no subject
Date: 2015-03-10 06:16 pm (UTC)Я к тому привёл пример, что если этот концепт эмплоить в полный рост, он серебряной пулей не станет, а вот ограничителем – да запросто.
С имеющимися в природе БД невозможно обеспечить ссылочную когерентность и, при этом:
— сделать хорошую платформу, подходящую для любого UI-сценария
— остаться в рамках ismorphic js.
Это нишевый концепт, не универсальный – а он очень стал внезапно переоценен. Зачастую просто от подмены реальных задач модным термином.
То-есть девелопер говорит "О, да вот же платформа, мы так легко всё сделаем, всего-то – нарисовать приложение, а сервер там сам разберётся", берёт платформу, и даже делает легко – а получается такое, что не скэйлится никак и ресурсы жрёт почём зря.
А потом наступает реальность и оказывается, что надо не просто дописать сервер, а вообще весь подход выкинуть и начать с начала.
no subject
Date: 2015-03-11 10:44 am (UTC)no subject
Date: 2015-03-11 11:01 am (UTC)1) Ссылка – это такой заинлайненный контрол, поэтому мы можем (по идее) на каждом получателе поддерживать её связь с эмитировавшим ссылку сервером и выводить, например, рядом со ссылкой индикатор загрузки.
2) Мы можем вместе со ссылкой отправлять маленькое превью картинки, а сервер – отдавать это превью с пометкой, что картинка ещё не загружена до конца.
3) Сервер может при обращении по ссылке пайпить на клиенты уже загруженную часть и досылать чанки по мере получения данных.
Есть и похитрее подходы, но это всё решения для UI, саму проблему некогерентности они не снимают.
no subject
Date: 2015-03-10 02:39 am (UTC)В самом деле есть ещё app UI degradation для тех или иных ролей пользователя. С этой точки зрения представление для гугла – это представление для читателя, без прав писать.
no subject
Date: 2015-03-10 01:52 am (UTC)no subject
Date: 2015-03-10 02:16 am (UTC)