JavaScript с момента своего появления проделал немалый путь от простых веб-страниц до полноценных приложений и даже серверов. Однако, чем сложнее становятся наши приложения, тем более остро встает вопрос подходящей для них архитектуры.
Вместе с Виктором gritzko Грищенко, создателем swarm.js (https://twitter.com/gritzko), рассмотрим современные подходы к построению архитектуры JS приложений как на сервере, так и на клиенте.
– Когда мы говорим о монолитных Web-приложениях, обычно имеется в виду архитектура, ставшая уже классической. Так называемый слоистый монолит хорошо прижился во многих корпоративных решениях. Расскажите, с какими недостатками данной архитектуры вам приходилось бороться в реальных проектах?
– С такой классической архитектурой я впервые столкнулся ещё в 2000 году в Банке России. Причём возникла она сама собой, в процессе аврального внедрения. Получился вполне обычный enterprise Java кошмарик, когда систему можно было запускать только целиком и только на сервере, с полной БД. Что-то сделать с получившимся монолитом было уже трудно, всё зависело от всего. Позже я видел такие же факапы в Яндексе. Это неизбежный этап, если приложение перерастает свою архитектуру.
– Как понять, что монолитный проект пора разделять на сервисы? Существуют характерные признаки?
– «Пора разделять» – это из серии «пора ампутировать». Разделение большой задачи на маленькие ортогональные подзадачки нужно осуществлять на этапе проектирования.
– Node.js очень активно развивается, статьи и руководства быстро устаревают. Существуют ли хорошие практики, на которые стоит равняться сегодня? Возможно, есть эталонные решения для построения микросервисной архитектуры?
Мне лично кажется, что микросервисы на основе REST – это те же миньоны, только в профиль. Так или иначе нужна асинхронная связь между подсистемами. В классике – это очереди сообщений, их используют везде и всегда везде использовали. Сейчас есть трендовые штучки – Kafka, Akka.
– Для монолитного приложения обычно достаточно иметь балансировщик нагрузки и нужное количество копий. Но в случае с микросервисами нужно также понимать, какой компонент системы следует масштабировать.
– Балансировщик как таковой решает проблему только для очень простых, в идеале stateless-приложений. Иначе начинаются проблемы синхронизации и конкурентного доступа к данным, в подвале открывается портал и из него лезут демоны. А компонент с одной понятной функцией однозначно проще масштабировать. Специализация и экономия масштаба – это вторая половина XVIII века, Адам Смит.
В общем-то, доклад не про микросервисы. Я прикладываю идеи «иммутабельной инфраструктуры» непосредственно к фронтенду, к тому, что работает в браузере.
– Хорошо, давайте обсудим код на клиенте. Какие проблемы сейчас актуальны?
– Например, при использовании модных фреймворков характерный размер клиентского бандла – это уже мегабайты JavaScript. Неприятненько, особенно на телефоне. Процесс сборки фронта – это уже целое большое дело.
Вытаскивание данных на клиента идёт полным ходом – сначала вытащили код, появились SPA, теперь постепенно вытаскиваем данные, хотим offline-first, быстрое время отклика и прочие плюшки.
В то же время, когда начинаешь говорить, что UI-компоненты должны быть чистыми функциями, это не считается странным. То есть, народ готов.
– Действительно, размер среднестатистического веб-приложения только увеличивается. У вас есть предложения, как можно улучшить ситуацию?
– Моя затея в том, чтобы строго разделить всё происходящее на фронтенде на (А) данные и (Б) всё остальное (компоненты, код). Причём, всё, что не данные – то функции. А функции – тоже данные, если подумать. Когда мы кладём код в систему контроля версий, он становится данными. То есть, у нас есть версионированные данные и версионированные функции, которые мы доставляем на клиент одинаково, одинаково кешируем и обновляем.
– А если чуть подробнее?
– Поясню на примере. Мы отправляем пользователю страницу, на ней сто кнопок. Мы не кладём код для ста кнопок в один большой бандл и не скачиваем всю базу данных, а отправляем только то, что нужно для рендера – часть данных и часть компонентов. Если пользователь начинает везде тыкаться – мы подтягиваем больше данных, больше компонентов. При этом то, что уже доставлено на клиента, кешируется навсегда. По необходимости обновляется.
Это похоже на микросервисы отчасти – этакое распиливание массы кода на иммутабельные/версионированные компоненты. Кстати, я лично предпочитаю всё-таки называть это версионированием, а не иммутабельностью. Версия, хоть данных, хоть кода, по определению иммутабельна, и фокус именно в этом.
В конечном счёте фундаментальная проблема здесь одна – и код, и данные должны работать на множестве устройств одновременно. Мы строим распределенные системы просто потому, что процессоры уже везде. Телефон – компьютер, книга – компьютер, телевизор – компьютер. Клиент-серверное мышление уйдёт в прошлое так же, как мейнфреймы ушли. А что придёт на смену – интересный вопрос.
– Звучит интересно, но как это совместимо с существующими библиотеками и фреймворками? Даже для частичной работы приложения потребуются большие основные зависимости (AngularJS, jQuery, ...).
– Ну, preact уложились в 3KB как-то.
Angular применять в таком контексте действительно незачем.
– Данная концепция уже сформировалась в отдельный проект? Где можно узнать подробности?
– Укладывается. Сейчас это скорее эксперименты. К декабрю расскажу, что получилось.
Кроме доклада Виктора на конференции HolyJS (11 декабря, Москва, Radisson «Славянская»), рекомендуем обратить внимание на следующие доклады:
- ECMAScript: latest and upcoming features
- Building Interactive npm Command Line Modules
- Лебедь рак и щука: как технологии тянут фронтенд на дно
- 3L3M3NT5
- Как подойти к современным веб-приложениям
- Debugging Node.js Performance Issues in Production
- WebVR is the next frontier
- A Little Closer to Frontend Bliss with Elm
- Performance Profiling for V8
- Remote (dev)tools своими руками
- Rich text editing with Draft.js
- Offline is the new Black
- Sharing files and data with friends using a P2P shared folder powered by javascript