9K

Как устроена архитектура Т⁠—⁠Ж

43
Как устроена архитектура Т⁠—⁠Ж

Привет!

Аватар автора

Вова Лазарев

менеджер разработки

Страница автора

Т⁠—⁠Ж выпускает статьи уже 5 лет. За это время из блога на Вордпрессе он вырос в большую самодельную систему из десятков проектов и инструментов. Мы, команда разработки Т⁠—⁠Ж, запускаем свой блог о том, как устроен журнал «под капотом», как мы его делаем, с какими проблемами сталкиваемся и как их решаем. Я в Т⁠—⁠Ж управляю разработкой и сегодня в общих чертах расскажу об архитектуре журнала.

Сайт Т⁠—⁠Ж работает благодаря разным сервисам:

  1. Движок сайта — Меркурий.
  2. Фронтовый монорепозиторий, или просто монофронт.
  3. Соцплатформа.
  4. Урания — образовательная платформа.
  5. Фичеры — калькуляторы, тесты и прочие интерактивы.
  6. Мобильные приложения.
  7. Медиасервис.
Вот так устроен Т⁠—⁠Ж изнутри
Вот так устроен Т⁠—⁠Ж изнутри

Дальше я расскажу, что делает каждый из этих сервисов и как они взаимодействуют друг с другом.

Меркурий

Главное бэкэнд-приложение Т⁠—⁠Ж — его движок. Собственный движок нам понадобился, когда журнал в 2017 году переезжал с Вордпресса. Движок назвали Меркурием в честь древнеримского бога богатства и торговли, потому что мы же журнал про деньги.

Меркурий собирает страницы, которые видит читатель журнала. Часть компонентов страницы он переводит в разметку и стили из собственных джанго-шаблонов: контент статьи, страницу о журнале и поисковую выдачу. За компонентами поновее — шапкой, подвалом, профилем и прочим — он ходит в сервис-прослойку, который уже отдает разметку и стили с нашего фронта.

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

Верстальщики набирают статьи журнала в синтаксисе html. Для удобства админка еще поддерживает шорткоды — это как теги, только они разворачиваются в более сложные синтаксические конструкции с особыми стилями. Cписок сервисов из начала статьи в админке записан шорткодом [ol type=milchin][/ol].

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

Когда в админке сохраняют статью, по ней проходит типограф. Это скрипт, который меняет часть конструкций в тексте так, чтобы они соответствовали правилам типографики и редполитики журнала. Например, проставляет знак рубля и неразрывные пробелы. Мы воспользовались готовым решением, только переписали пару правил, чтобы результат нам подходил.

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

В журнале встречаются страницы, которые объединяют другие статьи, например:

Правила, по которым собираются эти страницы, также обрабатывает Меркурий: это могут быть формулы, выставленные вручную редакторские настройки или их комбинации. Для таких настроек у нас тоже предусмотрены отдельные конфиги.

Разводящая раздела про путешествия. Сюда автоматически тянутся свежие статьи с этой темой, а блок подборок настраивает редактор
Витрина подборки. Статьи для нее собрал редактор
Результаты поиска, которые собираются автоматически. Сам поиск тоже реализован в Меркурии

В апреле 2020 года мы начали тестировать новую версию нашего движка, который мы назвали Ретроградным Меркурием. Мы полностью перебрали код, разбили его на несколько сервисов и затащили новые фичи: веб-сокеты в админке, улучшенный поиск и другие.

Когда переключим сайт на новый движок, подробнее расскажем, как и зачем его делали и что в итоге получилось. Это тема для отдельной большой статьи.

Монофронт

Когда мы окончательно переехали с Вордпресса на Меркурий, нам захотелось добавить интерактивности на страницы журнала. Возможности джанго-шаблонов тут ограничены, ведь только на них мы не смогли бы менять контент страниц в рантайме.

Чтобы не разрабатывать кучу кастомных страниц, решили вложиться в полноценный фронт — Монофронт.

Моно — потому что в один проект мы собрали:

  1. Интерфейс регистрации и авторизации.
  2. Комментарии.
  3. Футер.
  4. Хэдер.
  5. Отображение метаинформации материалов.
  6. Профиль пользователя.
  7. Промобаннер.
  8. Формы подписки.
  9. Страницу управления подписками.
  10. Образовательные курсы.

А еще в нем обрабатывается логика веб-трекинга пользовательских событий: откуда пользователь пришел на страницу журнала, что именно он на ней делает.

Вот как это выглядит в репозитории
Вот как это выглядит в репозитории

Как и все монолиты, Монофронт тоже скоро будет распилен на отдельные сервисы. В 2020 году мы хотим сделать из него несколько независимых приложений, вытащить простые виджеты в npm-пакеты, а сложные — в рантайм и поднять дизайн-систему. Мы уже делаем первые шаги в этом направлении, о которых потом обязательно расскажем.

Соцплатформа

В Т⁠—⁠Ж есть не только авторский, но и пользовательский контент. Пользователи регистрируются, комментируют статьи, подписываются, связываются с редакцией через специальные анкеты. Все это происходит благодаря соцплатформе.

Чтобы зарегистрировать или авторизовать пользователя, соцплатформа взаимодействует с АПИ соцсетей. У себя она сохраняет ценные пользовательские данные, в том числе персональные: имя, фамилию, почту.

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

У авторов журнала обычно два аккаунта. Один — в Меркурии, он определяет авторство статей. Второй — в соцплатформе, чтобы отвечать на комментарии. Думаю, в будущем перейдем на один общий аккаунт, а пока только немного облегчили жизнь редакторам: когда к аккаунту автора в соцплатформе привязывают аккаунт Меркурия, оттуда автоматически подтягиваются все его статьи.

Сообщество не получается развивать без правил. На каждое правило находятся нарушители. Поэтому соцплатформа предоставляет редакторам и модераторам интерфейс для проверки комментариев и бана нарушителей.

Модератор может удалить комментарий или замаскировать его часть, если она противоречит законам РФ или правилам пользования сайтом
Модератор может удалить комментарий или замаскировать его часть, если она противоречит законам РФ или правилам пользования сайтом

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

Часто мы предлагаем читателям поделиться чем-то с редакцией: рассказать о своей профессии, способе вести бюджет или о том, что люто бесит. Тут приходится рулить логикой сохранения ответов на вопросы форм и следить, чтобы они доходили до редакторов. Раньше гуглоформы мы заводили вручную и прикручивали проброс данных в них на фронте, а теперь автоматизировали это в соцплатформе

Редактор сам может завести новый опрос, редактировать вопросы, из которых он состоит, и собрать опрос из шорткодов
Редактор сам может завести новый опрос, редактировать вопросы, из которых он состоит, и собрать опрос из шорткодов

Урания

В 2019 году мы решили запустить собственную образовательную платформу. Сразу появились новые сущности: студент, курс, урок, экзамен. Чтобы их создавать и редактировать, разработали отдельный бэкэнд и админку — Уранию. Просто захотели продолжить тему античной мифологии. Но вообще чаще мы называем Уранию просто учебником.

Когда студент регистрируется, мы используем в качестве его идентификатора тот же аккаунт, что уже есть в соцплатформе. Сверху дописываем актуальную почту, курс, который студент проходит, и прогресс обучения. Так мы не смешиваем данные, но при этом можем просто синхронизироваться с соцплатформой. В тех же комментариях рядом используются данные из соцплатформы и учебника — например, чтобы нарисовать пользователю медальку курса, который он прошел. Эти данные удобно забирать по одному айди.

Контент самих курсов Урания пока берет из Меркурия. Курс состоит из уроков, а урок на самом деле — хитро стилизованная статья. С главными страницами курсов и со страницей всех курсов, которая скоро появится, Урания работает сама.

Главная страница курса «А как инвестировать»
Фрагмент урока о том, как выбирать брокера
В конце каждого урока — тесты для закрепления материала. В конце курса — экзамен, тоже в формате теста

Обычные тесты в журнале работают только на фронте, но в курсах мы проверяем ответы еще и на бэке. Учебник не только хранит вопросы, варианты ответов и правильные ответы, но и взаимодействует с тестами, потому что это отдельные приложения. Расскажу о них немного подробнее.

Фичеры

В Т⁠—⁠Ж выходят калькуляторы, карты, тесты, опросы и другие интерактивные материалы. Это такие отдельные фронтовые приложения. Мы разрабатываем их отдельной командой и не затаскиваем в Монофронт. Фичеры собираются в JavaScript-бандлы, а Меркурий подставляет их на страницы вместе с информацией о конфигах.

ip-calendar.js содержит всю логику интерактива, который помогает ИП вовремя сдавать отчетность. В полном адресе бандла на CDN содержится хэш. Так мы поддерживаем версионирование интерактивов
ip-calendar.js содержит всю логику интерактива, который помогает ИП вовремя сдавать отчетность. В полном адресе бандла на CDN содержится хэш. Так мы поддерживаем версионирование интерактивов

Большинство интерактивов используются максимум в двух местах: на отдельной странице и внутри статьи на ту же тематику. Но некоторые из них настолько прижились, что мы используем их десятки раз, например:

  • Тесты, которые помогают читателям узнать, какого богатства они достойны, или закрепить знания в образовательных курсах.
  • Опросы, которые разжигают дискуссии в комментариях.
  • Обменник валюты, который пересчитывает цену из местной валюты в рубли по актуальному курсу и подставляет значение в статью.
  • Эмодзи-сервис, который подставляет во все браузеры и на все устройства эмодзи из одного набора.
Обменник валюты переводит все цены в рубли по актуальному курсу. В редакции его называют валютным фичером
Обменник валюты переводит все цены в рубли по актуальному курсу. В редакции его называют валютным фичером
Пример опроса в конце статьи
Пример опроса в конце статьи

Мобильные приложения

Пока приложения, которые мы делаем, находятся на стадии разработки или закрытых бета-тестирований. Но кое-что рассказать уже можно.

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

Так выглядит урок курса об инвестициях в мобильном приложении. У приложения отдельный бэкэнд. Там мы обрабатываем контент, который есть только в приложении: аудио уроков и статьи для дополнительного чтения после курса. Этот же бэкэнд используем для пуш-уведомлений
Так выглядит урок курса об инвестициях в мобильном приложении. У приложения отдельный бэкэнд. Там мы обрабатываем контент, который есть только в приложении: аудио уроков и статьи для дополнительного чтения после курса. Этот же бэкэнд используем для пуш-уведомлений
Это первая версия мобильного приложения журнала. Пока главная фича — подписываться на подборки. Бэкэнд этого приложения хранит и обрабатывает пользовательские настройки и тоже отвечает за пуш-уведомления
Это первая версия мобильного приложения журнала. Пока главная фича — подписываться на подборки. Бэкэнд этого приложения хранит и обрабатывает пользовательские настройки и тоже отвечает за пуш-уведомления
1/2
Так выглядит урок курса об инвестициях в мобильном приложении. У приложения отдельный бэкэнд. Там мы обрабатываем контент, который есть только в приложении: аудио уроков и статьи для дополнительного чтения после курса. Этот же бэкэнд используем для пуш-уведомлений

Медиасервис

Основные медиафайлы в журнале — изображения. Медиасервис проверяет каждое изображение, перегоняет в webp и png, оптимизирует размер, обрезает под шаблоны из конфига и заливает на CDN. В ответ он получает ссылки на результат обработки — изображения разных форматов и размеров, которые получились из исходного. Остальные медиафайлы сервис просто бережно кладет на CDN.

Медиасервис особенно примечателен тем, что он использует модель serverless. Это не полноценный бэкэнд, развернутый на сервере или в облаке. Он лишь использует облачные функции Гугла и паттерн pub/sub.

Медиасервис использует соцплатформа для пользовательских картинок в комментариях и профиле. Этой функциональности пока нет на продакшене, но мы работаем над этим.

Что дальше

В следующих статьях я расскажу о каждом из сервисов подробнее, познакомлю с инфраструктурой журнала, процессами в продуктовых командах и инструментами, которые мы используем. Чтобы не пропустить, подписывайтесь в Телеграме.

Вова ЛазаревЧто осталось непонятным в этой статье? Что еще хотели бы узнать об устройстве Т⁠—⁠Ж?
  • Георгий ШабашевПочему в ответе на /api/stats/?uid=... не хотите возвращать еще и pretty-дату, чтобы html-кэш вчерашней статьи не вводил в заблуждение надписью "Сегодня, 10:15" Пример: https://journal.tinkoff.ru/permyaki-partners/9
  • тьфунатебя ¯\_(ツ)_/¯"Что осталось непонятным в этой статье?" - хорошая шутка (с) гуманитарий14
  • Никита ГалкинОчень интересно получилось. А на чём, если не секрет пишете мобильные приложения?2
  • Пончик АнончикОй, а расскажите про грейды юзеров: это будет или уже было и недоотключили? Я про "[раз откомментивший, хейтер]"3
  • Илья ПономаревХочется узнать про работу дизайнеров и сммщиков (потому что страна должна знать своих героев!)10
  • Макс ТретьяковА почему gcp? Вы отдельно живёте от инфраструктуры банка? Вижу, что гитлаб-то у вас тинькоффский.2
  • Вова ЛазаревНикита, React Native и TypeScript. Бэк мобилок на Node.js и TypeScript.6
  • SailingManОтписался от уведомлений об ответах на комментарии и лайках, а подписаться заново не могу, даже при изменении почты в профиле. Или это вопрос не к вам?)7
  • Вова ЛазаревМакс, если коротко: живём вместе, но не целиком на ней. У Тинькофф есть своя инфраструктура: железо, виртуалки, недавно завезли kubernetes. Там крутится Соцплатформа и часть инструментов, в том числе гитлаб. Инфраструктура Тинькофф раньше не подходила, потому что нельзя было гибко скалироваться, мало что работало "из коробки", были проблемы с поддержкой, не хватало плюшек, которые предлагали провайдеры на рынке. Многое из этого исправили. Пока не переезжаем, потому что ищем системного инженера :D https://journal.tinkoff.ru/team/system-engineer/. Об инфраструктуре ещё постараемся рассказать подробнее.3
  • Вова ЛазаревAndrey, хе-хе. Спасибо, знаем о проблеме. Как раз заканчиваем дизайн интерфейса управления подписками в профиле и уже начали разрабатывать эту фичу. О многих подобных проблемах мы узнали в марте благодаря опросу https://journal.tinkoff.ru/besit-tj/1
  • Вова ЛазаревГеоргий, первое, что приходит в голову — там динамические статы, а дата сама по себе статична. Если отдавать дату, которую отображаем в зависимости от времени запроса, то да, как вариант пофиксить проблему :) Хотим поправить с переключением движка.3
  • Мария ДолгополоваAndrey, могу подписать вас обратно — только скажите)1
  • Мария ДолгополоваПончик, не нужно вскрывать эту тему...6
  • Константин ПуляркинКруто! Долго залипал на схему. Она для меня еще сложновата, но все равно интересно. Еще, не понял почему в ней нет связи между мобильными приложениями и, например, Уранией. Интересно посмотреть на структуру баз.3
  • AndreiА исходники реакта специально не закрыли?0
  • SailingManМария, конечно да! А вот ещё вопрос, в моем профиле не отображается одна из статей, где я фигурирую. Это можно поправить?)1
  • Илья ПономаревUpi, вам не интересно, как работает ваше любимое издание (или одно и любимых)?9
  • GreLIAndrey, Можно просто в профиле смотреть. Уведомления — зло.0
  • Михаил ГородиловОчень интересно, спасибо!4
  • SailingManGreLI, это занимает много времени, а если ты оставил пару десятков комментариев и обсуждение продолжается - так и вообще нереально отследить.0
  • Макар ЗабиякинДобрый день! Может пропустил, а бек Mеркурия у вас на чем написан ? какой стек ?1
  • Пончик АнончикAndrey, я для этих целей себе atom feed по коментам запилил)2
  • Вова ЛазаревUpi, выделю две главные причины, зачем публиковать подобные статьи: 1. Мнение эксперта "снаружи", может оказаться весьма ценной. Вдруг мы что упускаем, или где-то неоправданно изобрели велосипед. 2. Тому, кто захочет у нас работать, будет проще принять решение, потому что у него на руках будет больше информации. Нам будет проще вводить в курс дела новичков. Материал нишевый, но есть читатели, кому интересно :)7
  • Пончик АнончикМария, взяли и убрали - а так красиво было(1
  • Мария ДолгополоваПончик, ВЫ ЕЩЕ МОЛОДЫЕ, ВАМ ВСЕ ЛЕГКО4
  • Дмитрий СемёркинЕщё немного технических подробностей и на Хабр - лишний способ показать себя и привлечь аудиторию. Очень даже целевую!2
  • Кирилл ПокрышкинПожалуйста, прикрутите нормальный поиск, знаю что это тяжело, но я в вас верю.4
  • Вова ЛазаревКонстантин, спасибо за замечание насчёт связи мобильных приложений и остальными бэкендами. Действительно, там клиентские части тоже, бывает, ходят в соцплатформу или Уранию. Но это больше зависит от самого приложения — гарантированно они ходят только в свои бэкенды, поэтому эти переходы на схеме опустили. Структура баз и связность данных — достаточно объёмная и специфичная тема. Возможно, некоторые подробности всплывут в других статьях. Не думаю, что в ближайшее время откроем это целиком.1
  • Вова ЛазаревКирилл, новую версию уже сдавали в закрытое бета-тестирование редакцией. Скоро сдадим ещё раз. Услышали, работаем над этим. Спасибо, что верите :)1
  • Вова ЛазаревМакар, текущий движок на третьем Питоне и Джанге, база — Постгрес.0
  • Вова ЛазаревAndrey, а что именно имеете в виду?0
  • Александр БонельА почему решили делать мобильные приложения? Я правильно понимаю, что планируете позиционировать их, как конфигуратор контента для пользователя? Просто незавидно, в чём их преимущество по сравнению с вашим вебом, который и так хорошо для мобилок адаптирован. Тем более, учитывая то, что вы на ReactNative пишете их.0
  • Александр БонельАлександр, *незавидно = не заметно0
  • Екатерина КрыловаВот бы Уранию в массы, с возможностью построить свой курс-учебник на такой платформе! Как преподаватель, интегрирующий интерактивные задания в свои курсы, но абсолютный новичок в разработке, не имею возможности, например, организовать мобильное приложение для интересного обучения.1
  • AndreiВова, сайт собран профиле разработки. Все исходники сайта видны..0
  • AndreiЕсли честно, то написан так себе - как будто писали фрилансеры..1
  • Вова ЛазаревАлександр, если у вас айфон, попробуйте скачать приложение учебника. Например, там есть аудиоуроки, которых нет в вебе. На Андроид чуть позже выпустим эту фичу. Приложения в целом будут ещё одним каналом дистрибуции контента. Некоторые читатели даже о них просят :)1
  • Александр ПлесовскихAndrey, это не «профиль разработки», а вполне себе продакшновый билд. А сорсмапы открыты чтобы вы наслаждались нашим прекрасным кодом и могли ругаться в комментариях.1
  • Александр ПлесовскихКстати, помимо исходников у нас ещё открыты и вакансии. Так что если вы готовы показать свой профессионализм и утереть нос нерадивым фрилансерам, то забегайте на огонёк.4
  • Евгений КарпельПончик, там буйной политоты и юношей бледных со взором горячим набежит - трусами не отмахаешься :)0
  • Пончик АнончикЕвгений, так ведь это в целях и заявлено, нет? "1. Мнение эксперта "снаружи", может оказаться весьма ценной. Вдруг мы что упускаем, или где-то неоправданно изобрели велосипед."0

Вот что еще мы писали по этой теме