Надеюсь вы уже прочитали пост про сравнение фреймворков и сделали правильный выбор. Теперь дело за малым — обуздать этого непокорного жеребца, освоить так сказать не паханную целену, ну в общем вы поняли. Предупреждаю, изучение 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 для синхронизации клиентов, потому что в 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).
Какие еще есть сложности?
Автор: vmakhaev