Firebase: прощання з ілюзіями

Маркетинг став частиною світу розробки. За кількістю зірочок на GitHub визначають, яке з схожих один на одного рішень крутіше, а за кількістю твітів можна спрогнозувати, яка технологія буде розвиватися в найближчі півроку. В таких умовах ми ризикуємо стати жертвами хайпи. Я — став: моє уявлення про Firebase розходилося з реальністю настільки сильно, що розуміння області застосування технології стало для мене справжнім одкровенням. Я хочу поділитися цим розумінням і тим, як все-таки використовувати Firebase правильно.



Бажання попрацювати з Firebase з'явилося у мене давно, але я чекав відповідного проекту. І дочекався: MVP системи бронювання офісів. Так як це MVP, бізнес-логіка бекенду досить примітивна. До того ж до Firebase буде підключатися мобільний додаток на iOS. З вигляду ідеальний випадок для використання сервісу, але в ході реалізації довелося зіткнутися з деякими проблемами, про які і піде мова далі.

Але спочатку хотілося б усунути всі непорозуміння. Ось дві речі, які потрібно засвоїти для роботи з Firebase:

  1. це не бекенд, а база даних. Можете забути про тих диво-прикладах додатків на Firebase без серверної частини, це поки недосяжне;
  2. це NoSQL з усіма його перевагами і недоліками.
Вибирати рішення для зберігання даних потрібно виходячи з природи самих даних. Firebase має свої недоліки:

  • область застосування набагато менше, ніж у NoSQL-рішення;
  • Firebase сильно обмежує вас при вибірці даних і при необхідності записати дані в декілька місць одночасно;
  • далеко не з усіма структурами даних зручно працювати в Firebase.
Але інструмент, що дозволяє швидко розпочати розробку MVP і має ще масу переваг, викидати на смітник шкода. А слабкі місця можна і залатати, якщо вони вам заважають.

Преамбула
Уявімо, що ви розробляєте систему бронювання для мережі готелів.



Там є такі сутності:

  • готель
  • номер
  • клієнт
  • бронь
Як реалізувати це на SQL-базі, зрозуміло: чотири таблиці зі зв'язками, і справа в капелюсі.

Як реалізувати це на NoSQL (Firebase)? Можна спробувати вкласти сутності в один-одного:

{
"готель": {
...
"номера": {
"номер": {
???
},
...
}
}
}

Тут починають виникати питання: а чи варто вкладати всі букинги в номер? а куди вкладати клієнтів? І т. п. Проблема NoSQL найчастіше в тому, що дані доводиться дублювати.

Є другий варіант: спробувати використовувати NoSQL схожим з SQL способом і створити докорінно об'єкти для кожної сутності, а зв'язку підтримувати, зберігаючи id інших об'єктів.

Ймовірно, в інших NoSQL-базах боротися з цими проблемами простіше, але рішення для своїх завдань я в Firebase не знайшов.

Який би варіант ви не обрали, у них є однакова проблема: неможливість зробити складну вибірку даних. Що робити, якщо ви хочете отримати список бронювань конкретного клієнта? Ці бронювання можуть виявитися вкладеними в різні номери і готелі, а якщо структура плоска, то Firebase не зможе відфільтрувати дані за кількома параметрами (цю проблему навіть обговорювали на StackOverflow). Загалом, якщо ви хочете зробити вибірку по клієнту і дати бронювання, Firebase SDK вам нічим не допоможе.

Можна спробувати вирішити цю проблему на бэкенде, але тоді вам доведеться викачувати вибірку даних, відфільтрованих по одному параметру, і фільтрувати її далі самостійно. Це неприйнятно.



Що робити?
Не використовувати Firebase для складної вибірки даних. У цьому нам може допомогти власний бекенд на Node.js і один з нижчеописаних інструментів.

ElasticSearch


Це пошуковий движок з JSON REST API, що використовує Lucene і написаний на Java. Подробиці можна почитати на офіційному сайті, а ми відразу почнемо розглядати його у зв'язці з Firebase.

Установка

Потрібно поставити ElasticSearch на сервер (зробити це по інструкції буде нескладно). Після потрібно інтегрувати його з Firebase, а саме — створити пошуковий індекс з бази Firebase. Я використовував офіційну інтеграцію від Firebase. Для запуску потрібно завантажити репозиторій, встановити залежності і заповнити config з ключами для Firebase.

У цьому рішенні я знайшов кілька мінусів:

  1. це окремий додаток на Node.js, і його складно пов'язати з бекендом;
  2. створити правильний індекс для ElasticSearch непросто, і однією синхронізацією даних з базою Firebase не обійтися;
  3. типи полів автоматично присвоюються.
Відчуйте їх на такому простому прикладі. У вас є список будівель з їх описом та координатами. ElasticSearch вимагає, щоб поля, що відповідають за географічні координати будівель, були оголошені такими заздалегідь, на етапі створення пошукового індексу. Інтеграція движка з Firebase не дає контролю над цим процесом, а просто синхронізує всі дані, автоматично визначаючи типи даних.

ElasticSearch безкоштовний і розгортається на своєму підконтрольному сервісі — це плюс. Але разом з тим виникає ряд проблем з деплоем і безпекою, які потрібно продумати заздалегідь.

Приклад: відкритий порт, використовуваний Elastic Search для зовнішніх запитів. Це створює вразливість, так як цей порт використовується для запису і управління пошуковими індексами. Можливим результатом такого недогляду стане видалення пошукового індексу або внесення до нього своїх даних. Тому спочатку цей порт відкритий тільки для запитів з тієї ж машини, на якій встановлено ElasticSearch.

Зробимо висновок: питання того, як реалізувати взаємодію між користувачем, ElasticSearch і бекендом, лягає на плечі розробника.

Algolia

SaaS-рішення для пошуку. Платне, але з безкоштовним планом. З прайсом і іншими деталями можна ознайомитися на офіційному сайті.

Інтеграція з Firebase реалізована за допомогою офіційної js-бібліотеки. Процес установки і запуску докладно описаний в readme, і у мене все запрацювало з першої спроби.

Виглядає інтеграція приблизно так:

var algoliasearch = require('algoliasearch');

...

var client = algoliasearch(config.algolia.applicationID, config.algolia.apiKey); // ініціалізуємо Algolia
var indexRooms = client.initIndex('rooms'); // ініціалізуємо пошуковий індекс Algolia

rooms.once('value', initInde); // rooms — це reference до об'єкта Firebase
function initIndex(dataSnapshot) {
var objectsToIndex = []; // Цей масив ми відправимо в Algolia
var values = dataSnapshot.val(); // Отримуємо значення snapshot'a
for (var key in values) { // обробляємо кожен room
if (values.hasOwnProperty(key)) {
var firebaseObject = values[key];
firebaseObject.objectID = key; // id об'єкта в Algolia повинен збігатися з ключем у Firebase
objectsToIndex.push(firebaseObject); // додаємо в масив
}
}
indexRooms.saveObjects(objectsToIndex, function(err, content) { 
if (err) {
console.log('error');
return;
}
console.log('success');
return;
});
}

В результаті ми отримуємо пошуковий індекс Algolia, що містить всі об'єкти rooms з Firebase. Зверніть увагу, що по ходу імпорту дані можна додатково обробити, наприклад підтягнути назва готелю з іншого об'єкта в базі даних.

Після того, як ми створили індекс, ми не збираємося оновлювати його цілком, тому надалі стежимо за подіями в Firebase і обробляємо їх:

rooms.on('child_added', addOrUpdateObjectRooms);
rooms.on('child_changed', addOrUpdateObjectRooms);
rooms.on('child_removed', removeIndexRooms);

Єдиний мінус у використанні Algolia в тому, що за SaaS потрібно платити. Але для MVP безкоштовного тарифу має бути достатньо, а робити на Firebase масштабний проект мало кому прийде в голову (я сподіваюся).

На противагу цьому сумнівному мінуса ми отримуємо зручну адмінку з доступом до аналітики, пошуковим індексом і нюансам роботи пошукових запитів.
Важливим плюсом є наявність SDK під все і вся — від мобільних платформ до фреймворків для бекенду. Суть я не вникав, але iOS-розробник сказав: це зручніше, ніж REST.

Я раджу вам спробувати саме Algolia: інтеграція з Firebase краще, установка простіше, а на додачу ми отримуємо консоль з аналітикою і SDK. Я залишив без уваги технічні деталі і не аналізував продуктивність і швидкість, це складна і окрема тема.

Підсумки
Вигоди цієї досить простої системи відчутні. Ми отримуємо:

  • Firebase для зберігання даних, всіх операцій читання і простих неконкурентних запитів;
  • Node.js для всіх конкурентних запитів і складної бізнес-логіки + обслуговування Algolia/ElasticSearch;
  • Algolia/ElasticSearch для пошуку і складною вибірки даних.
В наявності всі переваги Firebase, без недоліків у вигляді необхідності дублювати інформацію або організовувати складні і повільні вибірки на Node.js. При такому розкладі можна легко реалізувати систему бронювання або будь-яку іншу задачу, що вимагає транзакцій і складних вибірок даних. Наприклад, можна спочатку підібрати кімнату на конкретний день на двох осіб, з кондиціонером та балконом, а потім забронювати її і не боятися, що кімната вже була зайнята чи буде зайнята повторно. Деякі дані, щоправда, доведеться дублювати, але виключно в самому пошуковому індексі, а не бази даних.

За умови грамотного використання Firebase стає цілком прийнятним рішенням для доступу і зберігання даних. Завжди пам'ятайте, що первинні дані, і якщо ви вибрали неправильну структуру даних або спосіб роботи з ними, вас чекають серйозні проблеми в розробці.

Чекаю в коментарях ваших історій про інтеграцію Firebase і зауважень по статті. Спасибі!
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.