На клиенте используем стэк фейсбука:
- React.js. Один из самых популярных javascript фреймворков, интегрированный с остальными частями facebook-стэка.
- GraphQL. Протокол для доступа к данным, который адресует недостатки REST API.
- Relay. Клиентская библиотека для удобной работы с grapqhl-сервером.
- Flow type. Типизированное надмножество javascript, умеет выводить анонимные типы из graphql-запросов используя grapqhl-схему.
На сервере используем:
- [PostgreSQL][http://www.postgresql.org/]. Лучшая open source СУБД.
- node.js. Серверная платформа, которая может рендерить react-компоненты на сервере, позволяя создавать изоморфные приложения. Удобна для создания асинхронных webscoket и server sent events серверов.
- GraphQL reference implementation. Имплементация graphql для javascript.
В приложении есть три вида node.js серверов (один сервер может выполнять сразу три роли).
- GraphQL endpoint - отвечает на graphql запросы
- http endpoint - отвечает на http запросы к сайту (за исключением статики) и выдает html, сгенерированный на сервере реактом
- server sent events endpoint - оповещает клиентов об изменениях данных, произошедшых на сервере
GraphQL reference implementation умеет парсить и проверять запросы на корректность, а также преобразовывать запросы в набор вызовов функций resolve, которые должны получить данные для каждой вершины в graphql запросе. Функции resolve мы реализуем следующим образом: для каждой вершины в graphql схеме создадим view в postgresql базе, которое выдает необходимые данные. Наша имплементация функции resolve просто делает запрос к view. Плюсы такого решения:
- Высокодекларативный код
- Код view статически типизирован
- Упрощается коммуникация с доменными экспертами, которые знают SQL но не умеют программировать на javascript
- Код привязывается к персистентным данным, а не к javascript - технологии user interface. Код не устаревает при выходе очередной смене UI технологии
- Улучшенная security. Веб-сервер не имеет доступа напрямую к таблицам, он имеет доступ только к view, которые являются API базы данных (аналогично "геттерам" в ООП)
- Код замкнут относительно композиции. Можно из view строить более сложные view. Если мы храним запросы в javascipt коде в виде строк, то теряется возможность композиции запросов.
- Высокая производительность. При запросах все предикаты применяются в базе данных, сокращая количество IO. Решается N + 1 seleсt problem
На каждый возможный mutation содаем одноименную хранимую процедуру в postgresql. Плюсы такого решения:
- независимость от javascript
- security. Веб сервер не может напрямую писать в таблицы, только вызывать хранимые процедуры (аналогично "сеттерам" в ООП)
- не надо беспокоиться об открывании/коммите/откате транзакции. На node.js это сложно реализовать в виду асинхронности API и отсутствия исключений. Кроме того, не надо беспокоиться о prepared statement
Для реализации SSE используем postgresql logical decoding. К основному или hot standby postgresql серверу подключаются node.js сервера по streaming replication протоколу. Каждый сервер обслуживает определенный набор клиентов. Так как количество hot-standby серверов неограничено, и кроме того node.js сервера могут сами мультиплексировать streaming replication протокол, то система обладает негораниченной горизонтальной масштабируемостью. Каждый node.js сервер парсит протокол, извлекает изменения данных, на которые подписались клиенты, подключенные к этому серверу, и уведомляет клиентов об изменениях.