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.