Данные на фронтенде: шаг к приложениям будущего
Клиент-серверная архитектура для разработчиков веб-приложений — это примерно как одна из черепах, на которой стоял мир в воззрениях наших предков. Трудно себе представить иное положение вещей. Однако бесчисленное количество веб-приложений сформировало новую потребность — управление данными на фронтенде. Пока нет единого подхода и реализации, есть только отдельные технологии, позволяющие работать с данными на клиенте. Да и с ними никто особо не заморачивается. А между прочим, пора. О том, что уже есть в плане работы с данными на фронтенде и что будет дальше, мы поговорили с Никитой Прокоповым aka tonsky.
— Никита, привет! Расскажи пару слов о себе, чем ты занимаешься?
— Я работаю в компании Cognician, которая занимается веб-платформой для образования и тренингов. Я там занимаюсь фронтендом и частично бекендом.
— В аннотиции к твоему докладу на HolyJS написано, что ты Clojure-хакер…
— Это связано с тем, что я пишу на Clojure уже несколько лет, года четыре, наверное. Все мои последние работы были на Clojure, есть определённая экспертиза.
— Если пройтись по Хабру, то сразу заметно, что Clojure встречается значительно реже, чем другие языки программирования. В то же время он использует JVM, интегрируется с Java в обе стороны, имеет ещё ряд преимуществ. Что на нём можно делать и кому Clojure подойдёт?
— Да, действительно, у Clojure есть небольшие проблемы с популярностью по сравнению, например, с той же Scala, у которой в этом плане всё хорошо. А вот Clojure в тени. Тем не менее, это не какой-то маргинальный язык, он всё же относительно широко распространён. В нём есть несколько классных идей.
Clojure стал пионером иммутабельности «налево и направо», в нём есть классные примитивы для синхронизации многопоточных параллельных программ, он умеет компилироваться в JavaScript. Clojure и ClojureScript для JVM и JavaScript идут нога в ногу, можно писать код, который работает и там, и там. Это язык общего назначения, рекомендуется всем, кому не нужен супер низкий latency или супер предсказуемость, как в системах реального времени. Он постепенно находит своё применение и в корпоративном секторе — в Boeing и Walmart пишут на нём код, то есть довольно серьёзный бизнес принимает решение переходить на Clojure.
— Заявленная тобой тема доклада — «Данные на фронтенде». О каких данных идёт речь и кому это вообще нужно?
— Тут такая история — серьёзно фронтендом я начал заниматься примерно год с небольшим назад, до этого в основном был упор на серверную сторону. Сейчас идёт рост тенденции, что приложения можно писать в браузере и их там действительно можно писать. Но пока фронтенд-культура не доросла до тех подходов, которые давно существуют на сервере. Писать приложения на фронтенде — это большой фронт работ: что и как правильно делать. Какие-то проблемы уже понятно, как решать, какие-то — пока нет. Одна из таких проблем — как хранить данные? На сервере много вариантов: файловая система, база данных, можно в памяти хранить или в кэшах. А на клиенте не очень понятно: есть ad hoc решения (как бог на душу положит, сохранить где-нибудь в local storage или в переменных), можно пытаться применять базы данных, которые для клиента тоже есть, но их гораздо меньше. Приложений становится больше и больше, им нужно работать с данными. Это серьёзная задача, требующая системного ответа, поиска архитектуры. В докладе я как раз хочу рассмотреть, какие подходы уже есть, какие у них плюсы и минусы.
— А что случится с бекендом? Он останется или необходимость в нём отпадёт?
— Конечно, он продолжает своё существование, потому что веб-приложения эфемерны: ты зашёл-ушёл, но хочешь, чтобы данные твои сохранились. Остаётся клиент-серверная архитектура, возможно база данных-сервер-клиент, а может, в будущем когда-то появится и просто база данных-клиент, это тоже один из возможных вариантов. Наличие сервера — это один из самых интересных моментов: когда сервер работает с данными, он находится в тепличных условиях, а клиент находится в условиях диких, плюс на него смотрит пользователь. Как раз из-за этого возникает много проблем, как сделать хорошо. То есть хочется, чтобы приложение быстро грузилось, работало в оффлайне и т.д., а как это сделать — не очень понятно. Если напрячься и делать конкретно для себя, то может что-то и получится большими усилиями. Но рано или поздно, я думаю, появятся какие-то системные решения, которые можно будет взять «с полки» и использовать.
— Мне это сложно представить. Как-то привычно: есть приложение, например, какая-то веб-CRM, клиент передаёт данные на сервер, всё как всегда. А тут раз — и нужно управлять данными на фронтенде. Когда, при разработке приложений какого типа пора задумываться о данных на фронтенде?
— Если речь именно о приложении (графический редактор, что-то жутко интерактивное), то, как только сайт/приложение становится динамическим, сразу возникает необходимость управления данными на фронтенде.
— В связи с разговором об управлении такими данными, расскажи о DataScript. Кажется, это твой проект?
— Ну да, я основной разработчик. Были несколько пул-реквестов, мне помогали люди, но в основном это моя разработка. Его идея в том, что если данные структурированы и их относительно много, их удобно хранить в некоем подобии базы данных. DataScript — один из примеров базы данных, очень лёгкой. Это даже не в полной мере база данных, а скорее in-memory структура данных, по которой можно запускать запросы и в которой есть транзакции. Грубо говоря, это достаточно удобная структура данных (они реляционные, между ними есть связи), внутри всё хранится в плоском виде, индексируется. По такой структуре удобно делать навигацию, как по графу, вперёд-назад и удобно быстро находить нужные данные. Плюс, есть иммутабельность — можно делать архитектуры, которые на React делаются, когда state хранится в одном объекте, он иммутабельный и можно undo/redo делать, есть транзакции — можно создавать реактивные системы. Есть язык запросов Datalog со своим синтаксисом и возможностями. Нет только персистентности, DataScript работает в памяти.
DataScript нужен, когда есть сложное многомерное состояние и хочется на него смотреть с разных сторон, трансформировать. DataScript известнее всего в Clojure-среде (хотя поддерживает Clojure, ClojureScript, JavaScript и JVM) и часто используется в связке с Datomic на сервере. Мы используем DataScript в Cognician, чтобы представлять сессию клиента (вопросы, ответы, комментарии, сценарии), плюс поверх этого синхронизируем лог событий в Datomic (в обе стороны). Ребята из Precursor используют такой же стек для коллаборативного рисования графики (фигуры, объекты, группы), у них тоже решение для синхронизации свое самописное. Есть пара проектов, где в DataScript хранится множество мелких свойств объектов, проекты по инвентаризации. Есть интернет-магазин даже. Это очень удобная основа, когда данные лежат аккуратно в одном месте, чтобы писать поверх нее системное решение для синхронизации, например.
— Сейчас в руках разработчиков находится целый зоопарк баз данных. Что выбрать? Или оно без разницы?
— Документо-ориентированные — самый простой вид базы данных. Если ничего нет лучше, то нужно брать их. Если говорить конкретно о клиенте, то выбор совсем маленький: есть miniMongo, PouchDB, они обе документо-ориентированные. Можно писать на голом local storage. В идеале, нужно брать базу, которая даёт много возможностей — в частности, синхронизацию с сервером. Прозрачная двухсторонняя синхронизация с сервером снимет большую часть головной боли.
— Раз уж пошла речь о синхронизации… Реактивная синхронизация данных, Swarm.js — что это?
— Реактивная синхронизация данных — это когда у сервера есть новая информация и он сам понимает, какому клиенту её отослать. Пока никто нормального это делать не умеет. Есть решение на базе RethinkDB и Meteor — ты явно подписываешься на сервере на определённые объекты или коллекции и они приходят тебе server-push-ем. Это нетривиальная задача и возникает много проблем: во-первых, клиентов много, а сервер один, во-вторых, как поддерживать этот список подписок. Поток изменений на сервере постоянный — клиентов много, через сервер проходят все транзакции и сервер должен распознать транзакции и понять, кого оповещать, а кого — нет. Эти задачи эффективно решаются очень узкими технологиями, если вообще решаются.
Кажется, эта картинка скоро перестанет быть актуальной
И там же возникает вопрос консистентности — хотелось бы ещё, чтобы были какой-то порядок и полнота во всех этих объектах. Это, собственно, реактивная модель — тут и на сервере с этим относительно всё плохо, почти никакие базы данных не умеют нативно оповещать о транзакциях и изменениях. Эта проблема серверными программистами решается с помощью очереди: мы не пишем напрямую в базу, а складываем задачи в очередь, читаем из этой очереди и пишем — тогда все, кому интересно, тоже могут читать из этой очереди. Реактивная модель достаточно новая именно для архитектуры всей системы. То есть внутри языка программирования можно что-нибудь такое нагородить, а вот когда речь идёт о целой системе (данные, база, сервер, интерфейс пользователя), то тут подходы пока только нащупываются. Речь о том, как такую штуку собрать.
Сервер и клиент — распределённая система, в которой клиентов много, а сервер один. Клиенты могут писать и читать с сервера. И здесь возникает модель, когда у клиентов накапливается пул изменений и мы хотим, чтобы они попали на всех остальных клиентов. Чисто теоретически эта задача не решается в общем случае, но она решается красиво для определённых видов достаточно простых структур данных. Swarm.js — это библиотека, которая предоставляет структуры данных, для которых проблема синхронизации решается красиво, и протокол их синхронизации. Грубо говоря, если я зашёл на одну и ту же страницу Amazon с двух браузеров и в одном браузере добавил один товар в корзину, а в другом — другой, эти две корзины можно слить операцией объединения и конфликтов быть не может. Проблемы начинаются, если возможно, например, удаление — тогда сливать нельзя и нужно что-то придумывать. Есть такое новое веяние в распределённых системах — CRDT (Conflict-free Replicated Data Type), это типы данных, которые легко сливаются и Swarm.js — одна из реализаций таких штук. Это всё кусочки паззла, а глобальная цель у нас — сделать полностью реактивную систему, в которой любые изменения, которые происходят, распространяются по всей системе, причём система состоит из базы, сервера и множества клиентов. IT находится в поиске такой системы. Скорее всего, это будет не какое-то готовое решение, а подход, следуя которому можно уже создавать архитектуры.
Послушать Никиту, подискутировать с ним, а также с остальными докладчиками, рассказать о своих находках и мыслях о будущем можно будет на конференции HolyJS, которая пройдёт в Санкт-Петербурге 5 июня 2016 года.