Pull to refresh

Tutorial Derby.js

Reading time 5 min
Views 16K

Надеюсь вы уже прочитали пост про сравнение фреймворков и сделали правильный выбор. Теперь дело за малым — обуздать этого непокорного жеребца, освоить так сказать не паханную целину, ну в общем вы поняли. Предупреждаю, изучение Derby вызывает изменение парадигмы. Мир веб-разработки для вас больше никогда не будет прежним.




Вместо предисловия



Пост написан по мотивам вопросов в комментариях и сообщениях на Хабре.
Полагаю что вы уже знакомы с Derby и создали своё первое hello world. Если нет, то крайне рекомендую это сделать прямо сейчас. А вот несколько примеров приложений с комментариями. Ибо здесь мы будем говорить об общих, сложных для понимания моментах.
В примерах кода — Coffeescript. Надеюсь у людей знакомых только с js не будет проблем с этим.
Также в примерах я использую ид в виде 12341243. в реальности это будет guid.

Приложение Derby



Каждое приложение Derby — это расширенное приложение Express. То есть вы можете использовать connect.js, серверные routes и т.д., как вы это делаете в обычном Express приложении.
Одно Derby приложение состоит из одного серверного приложения (Express) и одного или нескольких клиентских. Например вы можете разделить сайт для пользователей и админку, и не загружать пользователям темплэйты и бизнес-логику от админки.

Первый запрос



При первом запросе от клиента на сервере обрабатывается нужный route и сгенерированный html отправляется на клиент вместе с клиентской частью Derby приложения (темплэйты, model, browserify с модулями, бизнес-логика). Клиент первым делом видит html, а всё остальное подгружается следом. В дальнейшем, при изменении url, routes будут отрабатываться на клиенте и генерировать html там же. Если js в браузере отключен, то routes отрабатываются только на сервере и html генерируется всегда на сервере, как обычный статический сайт.

Routes



Routes работают как на сервере, так и на клиенте с помощью tracks. Если на клиенте не находится нужный route, то запрос просто проваливается на сервер. То есть на сервере может быть больше routes, чем на клиенте, например, для загрузки файлов и т.п.

Html



Derby имеет встроенный шаблонизатор — немного измененный Handlebars. Есть модуль для Jade.

Связка с данными осуществляется так:
динамическая (данные изменяются при изменении html и наоборот)
<input type="text" value="{_page.name}" />, <h2>{users.12431243.age}</h2>

статическая (вывели и забыли)
<input type="text" value="{{_page.name}}" />, <h2>{{users.12431243.age}}</h2>


Темплэйты состоят из секций: Tille, Head, Body, Footer, Scripts и др. Вы можете, например, в layout-темплэйте задать Head и Scripts (если они одинаковые для всех страниц), а в других задавать только Title и Body.

Model



Model — это объект с api для работы с данными. Все манипуляции с данными происходят через него.
Model может существовать как на сервере, так и на клиенте. На сервере может быть несколько моделей, на клиенте — только одна.
Модели на сервере можно создавать для каждого запроса
model = req.getModel()

либо не зависимо от запросов из store
model = store.createModel()

на клиенте модель всегда тут:
model = require('derby').app.model


Если модель создается для первого запроса от клиента, то после всех манипуляций с ней, она сериализуется и отправляется на клиент. В противном случае на клиент отправляется пустая модель.

Path



Все данные представлены в виде path, который состоит из названия коллекции, ид и свойств данного объекта.
Например:
user = model.get 'users.12341234'
userName = model.get 'users.12341243.name'
model.set 'customers.12341234.properties.isIdiot', true
model.set 'customers.12341243.properties', {justMadeOrderForBillionDollars: true, isIdiot: false}


Fetch, Subscribe



При создании model — пустая.
emptyObject = model.get()

Есть два способа заполнить ее данными fetch и subscribe
fetch — просто достает данные из бд и помещает их в модель.
model.fetch 'product.12341243', 'users', -> console.log 'теперь у нас в модели продукт и все пользователи'

subscribe — динамически связывает данные из бд с данными в модели
model.subscribe 'products', -> console.log 'все продукты синхронизированы'

Теперь если мы поменяем данные в модели
model.add 'products', {name: 'Awesome brand new product'}

То эти изменения запишутся также и в бд. И если есть другие модели (на других клиентах или на сервере), подписанные (subscribe) на эти данные, то изменения также добавятся в эти модели.

Queries



Вам не обязательно подписываться на всю коллекцию. Можете подписаться на определенную выборку с помощью queries.
ids = model.get '_page.ids'
$customersWithSomeIdQuery = model.query 'customers', {'id' => { "$in" => ids}}
model.subscribe $customersWithSomeIdQuery, ->
  myCustomers = model.get $customersWithSomeIdQuery


Для каждой бд синтаксис queries будет свой. В данном случае Mongo Queries.

Store



Объект на сервере в единственном экземпляре, из которого создаются все модели и в котором происходит вся магия.
Store связан со всеми моделями. Для связи с моделями на клиентах используется browserchannel. Веб-сокеты не используются, потому что не гарантируют порядка доставки сообщений, что крайне важно для OT. browserchannel — это «socket.io от Google», также используется в Gmail.
Данные с клиентов мержатся в бд с помощью Share.js, это реализация Operational Transformation. Позволяет разрешать конфликты, учитывая версию данных. Клиенты могут уходить на долгое время в оффлайн, изменять данные в своих моделях, после этого их данные запишутся в бд с учетом изменений от других клиентов за это время. Это очень похоже на то как в своё время работал Google Waves (OT в share.js и Google Waves пишет один человек).

Live-db



Live-db — это хранилище данных для share.js. Состоит из двух частей:
1) Redis — обязательно. Share.js использует redis pub-sub для синхронизации клиентов, потому что в pub-sub в mongo вводит ряд ограничений (коллекции должны быть определенного размера), по этому redis со своей скоростью и отсутствием ограничений идеален для этого. Также в redis находится кэш последних операций (сами задаете сколько). Этот кэш как раз и позволяет мержить данные с клиентов ушедших в оффлайн на долго.
2) Сами данные могут храниться в любой базе данных. Но на данный момент есть адапетр только для Mongo DB. Написать адаптер довольно легко.
Историю всех операций можно не хранить, можно хранить в той же бд, что и данные, можно хранить в другой бд.

Архитектура



Снизу вверх:
live-db — это обвертка над redis и mongo чтобы сделать событийную бд.
share.js — это OT, используя live-db
racer — это работа с данными: store, model. На основе share.js
derby — это routes (tracks), templates, views.

Вы можете отдельно использовать live-db, share.js, racer, tracks и др части. Вы не можете использовать racer отдельно от share.js, либо share.js отдельно от live-db. Вы не можете использовать derby отдельно от racer, ибо derby привязано к модели данных racer.

Масштабирование



Вы можете запускать несколько Derby приложений (каждое со своим store), работающих с одной бд (live-db).

Какие еще есть сложности?

Материалы по Derby.js
Tags:
Hubs:
+11
Comments 83
Comments Comments 83

Articles