Kvakka.com: пусть дети поменьше задротят в онлайне

В начале 2018 года мы выпустили в открытый доступ проект Kvakka.com – социальную сеть, маркетинговую площадку и дейтинг для фанатов настольных игр. Позади три года разработки, за которые сервис вырос из пет-проекта в коммерческий сервис, пережив две смены фронтенда. Перед нами стояла великая миссия – вытащить детей и подростков из дома, из онлайн-игр, в реальный мир, где они смогут встречаться лицом к лицу с себе подобными. И находить друзей, подруг, партнёров по играм и не только.

Я создатель этого проекта, системный архитектор, и ведущий разработчик в одном лице. Я не только руководил разработкой, но и своими руками спроектировал и написал весь бэкэнд и кусок фронта, а так же поставил все типовые процессы разработки от CI и автоматизированного тестирования до деплоя и мониторинга. Сегодня я расскажу вам о том, как этот проект устроен внутри, и почему это круто.

Kvakka родилась на фундаменте лучшего в мире инструмента для веб-разработки – Ruby on Rails. Ведь только Rails официально ставит своей первостепенной целью – удовольствие разработчика, а не абстрактные паттерны или компоненты.

Проект рождался сразу по принципу API first, и таким образом мы, его создатели, сразу “из коробки” получили не только API для веб-интерфейса Single page application, но и API для будущих мобильных приложений, и для интеграции со множеством наших партнёров. Ведь сегодня не важно, насколько ты крут в изоляции от остального мира, – важно то, насколько крута экосистема вокруг тебя.

Мы делаем ставки на очень широкую и богатую экосистему, которая будет приносить ещё больше ценностей нашим пользователям. Именно поэтому мы строго соблюдаем принципы REST, которые не просто формализуют стиль API, но и являются де-факто стандартом мировой индустрии. Именно потому, что позволяют легко интегрировать с нами любой современный продукт или фреймворк – без необходимости долго изучать запутанную документацию множества кастомных методов, как это сделано у большинства менее удачных проектов.

Структура базы данных Квакки тоже изначально была заточена под Business Intelligence – мы сразу понимали, что скорее рано, чем поздно, к нам придут бизнес-аналитики и маркетологи. А сегодня они – это люди, которые питаются данными на завтрак, обед и ужин. В отличие от многих более простых проектов, в которых модель данных строится “как сложилось”, а потом к ней прикручивается аудит, версионирование, и аналитика, – в Квакке изначально business critical data подвержены хронологированию и аудиту изменений.

Нам не нужно прикручивать к системе какой-то сторонний аудит, чтобы знать, какое в среднем время проходит между запросом на участие во встрече и одобрением участия организатором – в проекции на всех организаторов системы – у нас это есть by design, легко и изящно. Особенное внимание мы уделяем контактным данным: если недоброжелатель укажет чей-то чужой номер телефона, а потом поменяет его или даже удалит в интерфейсе – в системе всё равно останется полная хронология изменений.

Мы относимся к данным с трепетностью настоящей системы управления контентом. Например, у нас геоточка или время начала встречи – это отдельные независимые объекты. Их можно переслать по внутренней переписке, их можно лайкнуть, их можно выставить на голосование – если вы хотите, чтобы ваша аудитория выбрала время и место. Точно также мы относимся ко всему, с чем мы работаем.

В основе системы лежит событийная модель – более развитая нами известная модель акторов (actors model). Каждый объект в системе (будь то пользователь, встреча, геоточка или комментарий) уведомляет всех о своих изменениях, а другие объекты “слышат” эти уведомления и могут предпринимать последующие действия. Таким образом, вместо tight-coupled “лапши” объектно ориентированного программирования (где все функции жёстко связаны между собой в нерушимый монолит) – мы имеем очень гибкую систему независимых микрокомпонентов, которые легко поддаются автоматизированному тестированию, масштабированию и отладке.

Именно благодаря этому принципу, большинство длительных операций легко и изящно выносятся у нас “в фон”: уверен, что вы даже не представляете, сколько всего происходит в системе, когда один пользователь ставит другому лайк. Начиная с того, что нам нужно пересчитать их взаимные интересы, и… миллион всего. Конечно, вы не будете ждать этого в петле HTTP-запроса, операция для вас выполнится мгновенно, – а всё остальное будет в фоне, через событийный поток, причём параллельно на нескольких физических машинах. Мы доработали штатный ActiveJob, чтобы связывать события лёгким и человекочитаемым синтаксисом.

Мы воспроизвели те же принципы и на фронтенде. Мы спроектировали и написали полностью свой ORM под Angular-ом, чтобы иметь возможность работать с данными а-ля rails style прямо в яваскрипте: у нас можно в шаблоне фронтенда написать “post.comments.last().author.city.title” – и фронтенд сам сходит достанет нужные данные, положит их себе в реестр и будет следить за их жизненным циклом. Естественно, работают все реляции типа has_many так же, как они работают на бэкэнде. Мы привнесли сюда очень много полезных паттернов из Backbone и Ember.

Более того, бэкэнд и фронтенд у нас объединены в двустороннюю систему: ведь на современном интерактивном сайте данные не только поступают с фронтенда на бэкэнд, но вам нужно доставлять другие изменения обратно! И снова хвала событийной модели – те же триггеры, которые работают при изменении данных в БД, вызывают и доставку данных на фронтенд.

Но не думайте, что это всего лишь типичный websocket channel “из коробки”: нет, это гораздо более сложный механизм, ведь данные ограничены правами доступа, и разным пользователям они доставляются в разном виде. Кроме того, мы учитываем и кратковременные обрывы связи: если наш пользователь с мобильным телефоном заходит в лифт и связь обрывается, система приложит все усилия, чтобы “дослать” ему все произошедшие изменения в момент, когда он снова подключится к сети.

Конечно, мы автоматизировали и традиционный цикл разработки и релизов. У нас применяется continuous integration, автоматизированные тесты, докер, инструменты деплоя и мониторинга, принципы git flow. Мы получаем уведомление каждый раз, когда что-то “ломается” на продакшене. В таком случае, тут же выпускается багфикс, который выкатывается в ближайшем релизе – и как правило абсолютно незаметно для наших пользователей.

Мы очень многое знаем о наших пользователях. Мы знаем, что город – это не атрибут пользователя, а связь many-to-many – потому что люди склонны переезжать из города в город, а мы хотим знать их историю. Мы работаем над мобильными сервисами и гео-функциональностью, чтобы однажды человек, написавший коммент про Каркассон, тут же получил бы на телефон пуш-уведомление с персональной скидкой на коробку Каркассона в ближайшем магазине за углом. Мы знаем, какое у вас настроение, как оно меняется, и какова в целом эмоциональная реакция комментаторов на ваш пост, – потому что работаем над sentiment analysis каждого вашего текста. Мы знаем, кто загружает котиков, а кто собачек, потому что распознаём картинки при помощи AI. Мы знаем очень многое, а хотим знать ещё больше – потому что верим в то, что это знание даст вам лучший жизненный опыт, лучшие переживания и лучшие впечатления от настольных игр, чем когда-либо ранее.