
Вступление: Драконы и золото
Старейшины говорят, что это история о вертикальном и горизонтальном масштабировании SaaS-приложения, произошедшем за 10 лет, но... Я таких слов не понимаю, слишком заумно. Для меня это история о золоте, драконах с всё возрастающим аппетитом и фермерах, которые кормят этих драконов.
Что вообще у нас за отношения с драконами? Если коротко, то мы кормим драконов продуктами в обмен на золото. В целом процесс очень простой: дракон прилетает к городу, говорит какое блюдо он хочет. Потом из города на ферму бежит гонец за сырыми продуктами и возвращается с целой телегой. Дальше, уже в городе, мы эти продукты сортируем, готовим, упаковываем, и наконец, отдаем дракону готовое блюдо. Потом второй заказ, третий, десятый…
Порой драконы прилетают один за одним, порой одновременно, большие и маленькие. Драконы растут, так же как и их аппетит... Если не предпринимать никаких действий, то рано или поздно даже самая плодородная земля не сможет прокормить гигантского дракона. А уж когда прожорливых драконов становится много, то тем более нужны новые решения!
Глава 1: Один город, одна ферма
Мир изменился. Я чувствую это в воде, чувствую это в земле, ощущаю в воздухе... Но что же было в самом начале?
У нас был совсем небольшой город и небольшая ферма. Несколько драконов были нашими постоянными клиентами, и одной фермы вполне хватало на всех. Каждый день мы забирали продукты на ферме, перерабатывали их в городе и отдавали готовые блюда. Драконы были довольны и платили золотом. Все были довольны. Казалось, так будет продолжаться вечно…
Вскоре наш главный дракон вырос ещё больше и стал требовать существенно больше еды, более быстрой доставки и новых блюд. Попутно стало появляться всё больше и больше новых драконов, их стало десять, сто, тысяча...
Наш город переставал справляться. Всё, что мы делали — мы делали в городе; ресурсов и времени на всё не хватало, совершенно разные задачи решались на соседних улицах. Время приготовления пищи увеличивалось, порой драконы улетали голодными, мы были в шаге от катастрофы.
Мы понимали, что наш город скоро сгорит от гнева драконов, поэтому совет старейшин собрался на экстренное заседание. Что-то нужно было менять, иначе мы рисковали потерять и золото, и город, и, возможно, наши жизни.

Стартовая архитектура представляла собой классическое монолитное приложение на Java с единой базой данных PostgreSQL. Вся бизнес-логика, обработка запросов и взаимодействие с клиентами происходили в рамках одного сервиса. С ростом числа пользователей и увеличением нагрузки монолит столкнулся с проблемами производительности, что привело к необходимости архитектурных изменений.
Глава 2: Разделение сельхозугодий
Мы быстро поняли, что одна ферма не сможет прокормить всех драконов: сборка продуктов с фермы становилась всё медленнее, а объемы наших амбаров стали подходить к критическим размерам.
"Нужно разделять сельхозугодья!" — предложил мудрейший из старейшин на очередном собрании.
Так появились два типа ферм: Профиль и Шарды. Каждый дракон приписывался к конкретному полю-шарду, и все его персональные продукты хранились именно там. Профиль стал центральной фермой, хранящей общую информацию обо всех драконах нашего королевства и священные записи: информацию о том, к какому полю-шарду приписан каждый дракон. Шарды же стали специализированными полями, каждый из которых обслуживал только определенных драконов.
"Представьте, что мы разделили нашу ферму на отдельные поля. Общая информация — в центре, а на конкретного дракона работает только одно поле. Проблемы на других полях теперь никогда не будут волновать дракона. Мы засеваем новые сельскохозяйственные территории вместо попыток выжать больше с имеющихся", — объяснял я юным фермерам.
Это решение принесло множество преимуществ. Во-первых, при проблемах с одной фермой-шардом страдали лишь драконы, приписанные к нему, а не все королевство. Во-вторых, для пары особо прожорливых драконов мы выделили отдельные эксклюзивные фермы, чтобы они не мешали другим. В-третьих, добавление новых шардов-ферм стало стандартной процедурой горизонтального роста. Мы понимали, что фермы перестали быть бутылочным горлышком, мы могли добавлять новое поле хоть каждый день.
Самое сложное, чему нам пришлось научиться - это разделение одного поля-шарда на два, потому что в этом случае нам приходилось собрать всё, что принадлежит конкретному списку драконов, и перенести на другое поле. Нам пришлось очень точно контролировать, какие вещи принадлежат одному дракону, а какие — другому, чтобы иметь возможность переносить продукты.
И это работало. Наша инфраструктура стала более устойчивой, и новые драконы прибывали.

База данных была разделена на две логические части: Profile, содержащую общую информацию и маппинг аккаунтов к шардам, и Shards — набор баз данных, каждая из которых хранила данные определенной группы клиентов. Данные одного клиента (одной компании) принадлежали только одному шарду. Такая архитектура позволила изолировать проблемы: выход из строя одного шарда затрагивал только клиентов этого шарда. Кроме того, появилась возможность горизонтального масштабирования через добавление новых шардов и перераспределение клиентов между ними.
Глава 3: Один город, много поселений
После преобразования ферм на первое место по важности вышли проблемы города. "Нам нужны новые поселения!" — объявил Старейшина на собрании, и так началась эпоха первых независимых поселений.
Рядом с центральным городом как грибы стали расти небольшие специализированные посёлки. Каждый отвечал за что-то конкретное: один занимался рассылкой писем для драконов, другой — рисовал и хранил их портреты, третий — анализировал потребление пищи. Часть жителей уехала из города в эти поселки, так что в городе стало больше места. Драконы, кстати, про эти поселки ничего не знали, они по-прежнему всегда обращались в город, а администрация уже внутри разбиралась, как работать с конкретным запросом дракона и нужно ли отправлять весточку в поселения.
Поселения работали независимо от основного города, взаимодействуя только через волшебные дороги. Если один из поселков сталкивался с проблемами, это не влияло на работу всего королевства. Мы могли перестать доставлять какие-то информационные письма для драконов, но основные блюда всегда поставлялись безотказно.
Кроме того, каждое поселение управлялось независимо: eсли какая-то конкретная услуга становилась особенно популярной среди драконов, мы строили больше домов в соответствующем поселении, а основной город мог об этом даже не знать.

Вокруг монолитной части были созданы выделенные сервисы: независимые maven-модули внутри моно-репозитория с независимым CI/CD-процессом. Эти сервисы не имели выделенных баз данных и не общались напрямую с клиентом. В основном они обрабатывали задачи из очередей RabbitMQ. Это позволило разгрузить монолит от второстепенных задач, сохраняя при этом централизованную обработку основных пользовательских запросов.
Глава 4: Заморские колонии
Наш первый, главный город располагался в Америке, но драконы прибывали со всего мира. Лазурные драконы из-за океана жаловались на задержки в доставке пищи — длинный путь через море замедлял процесс. К тому же, европейские драконы требовали, чтобы их продукты хранились только на европейской территории согласно их драконьим законам.
"Нам нужны заморские колонии-государства!" — объявил совет старейшин. И мы начали возводить их.
Сначала появилась колония в Европе — отражение нашего основного королевства. Там были свои фермы с профилем и шардами, свой город и поселки. Европейские драконы теперь прилетали исключительно к своей колонии, получали пищу значительно быстрее, вся информация хранилась согласно их законам, а мы получали еще больше золота.
Затем возникла необходимость в облачной экспансии, и мы построили еще одно королевство в облаках — эфемерное, но не менее могущественное.
"Три абсолютно независимых королевства: в Америке, Европе и облаках - каждое со своими законами, но с единой валютой и общим правителем", — так рассказывали странники, посещавшие наши земли.
Такая структура позволила нам охватить драконов со всего мира и работать со Старым и Новым Светом независимо. Если случалась катастрофа в одном королевстве, другие продолжали функционировать.
Однако с расширением границ возникли новые сложности. Порой требовался перенос продуктов из одного королевства в другое, обеспечение единых стандартов обслуживания драконов, управление тремя идентичными, но независимыми системами — все это требовало новых решений и подходов. Если раньше мы переносили данные драконов только между полями-шардами, то теперь схожую операцию приходилось порой делать между колониями.


Были созданы независимые дата-центры в разных географических зонах: американский и европейский на собственном железе, а третий в GCP (Google Cloud Project). Вся инфраструктура и архитектура во всех локациях была идентичной, отличалось только количество шардов. Это решение обеспечило соответствие региональным требованиям по хранению данных, снизило задержки для пользователей из разных регионов и повысило отказоустойчивость всей системы, позволяя продолжать обслуживание клиентов даже при полном отказе одного из дата-центров.
Глава 5: Реорганизация города
С ростом количества шардов центральный город стал превращаться в бутылочное горлышко. "Нам нужно реорганизовать сам город!" — предложил главный Старейшина, и так родилась концепция Региона и Сегментов.
Волшебники нашего королевства открыли удивительный секрет — они смогли создать отражение основного города в параллельных мирах! Основной город мы назвали Регионом, а его отражения в других измерениях — Сегментами.
"Представьте, что основной город существует одновременно в нескольких пространствах", — объяснял я новичкам. "Регион находится в центральном измерении, видимом всем драконам. Но каждый дракон, приближаясь к городу, переходит в своё особое измерение — свой Сегмент, где всё устроено так же, но обслуживает только определённую группу драконов и работает только с определенным списком шардов".
Обычно для каждого сегмента выделялось по 5-10 шардов, но для VIP-драконов мы создавали не только персональный ферму-шард, но и персональное измерение: "Они платят столько золота, что заслуживают собственной реальности", — отмечал королевский казначей.
Новая организация принесла множество преимуществ. Проблемы в одном измерении затрагивали только драконов этого измерения. А когда мы видели, что драконов становилось слишком много для одного пространства, мы создавали новое измерение-сегмент и новые шарды для него.
Важно понимать, что город остался единым по сути, но стал существовать во множестве параллельных пространств. Мы научились расти, не теряя в надёжности, и обслуживать больше драконов, не усложняя систему управления.

Монолитная часть приложения осталась единой с точки зрения кода, но были созданы два типа артефакта — Region и Segment. В каждом дата-центре мог быть только один Region и множество Segments. Основное отличие заключалось в маршрутизации запросов: общие запросы направлялись в Region, а запросы, ассоциированные с конкретными пользователями — в Segment. Каждый Segment был ассоциирован только с определенным набором шардов. Такая архитектура обеспечила предсказуемость обработки трафика, улучшила изоляцию отказов и упростила горизонтальное масштабирование за счет добавления новых сегментов. При этом, разумеется, и Region, и каждый из Segment работали в несколько инстансов.
Глава 6: Эпоха независимых городов
С ростом нашего королевства появлялись новые задачи и вызовы. Некоторые из них было сложно вписать в существующую архитектуру Региона и Сегментов. Так начали появляться первые полностью независимые микро-города.
"Представьте, что вместо расширения существующего города мы строим новые специализированные города с нуля", — объяснял старейшина. "Каждый такой город занимается только одной конкретной задачей, но делает это превосходно".
Так появились города Аналитики, Агрегации данных, Мобильный город и другие. Каждый мог иметь свою инфраструктуру, свои фермы, свой жизненный цикл. Они общались с основным городом и между собой через голубей и телеграф. Драконы могли прилетать напрямую в эти микро-города.
Они дали нам невероятную гибкость. Если один город сталкивался с проблемами, остальные продолжали работать. Каждый город развивался независимо, новые технологии можно было внедрять, не меняя всю архитектуру.

К этому моменту американский дата-центр включал уже более 150 независимых сервисов и более 25 шардов и 4 сегмента. Рядом с монолитной архитектурой были созданы полностью независимые микросервисы. Основной подход заключался не в том, чтобы разделить существующий монолит, а в создании новой функциональности в виде отдельных сервисов. Коммуникация между сервисами осуществлялась как через HTTP-вызовы, так и через gRPC.
Глава 7: Подготовка к будущему
С годами наше королевство достигло впечатляющих масштабов. Тысячи драконов ежедневно потребляли наши услуги, золото текло рекой, технологии развивались. Но мы никогда не останавливались на достигнутом.
"Какой следующий шаг в эволюции нашего королевства?" — этот вопрос постоянно обсуждался на совете старейшин.
Ответ пришел после одного крупного инцидента. Даже в нашей распределенной архитектуре некоторые проблемы с новой функциональностью могли затронуть базовые возможности. Драконы не могли получить доступ к своей основной пище из-за проблем с десертом.
"Нам нужно разделить каждый Сегмент на две части: Ядро и Периферию", — предложил я после долгих размышлений.
Ядро (Core) — это минимальный набор критически важной функциональности, который должен работать всегда, несмотря ни на что. Периферия (High-risk) — это все дополнительные возможности, которые могут временно выйти из строя без катастрофических последствий. Самое чудесное в этом то, что Ядро и Периферия были всё тем же Сегментом! Мы просто разделили эндпоинты в очередной раз, сказав, что часть функциональности теперь особенно критична.
Такое разделение также помогло нам гибче распределять ресурсы и приоритеты: первое — стабильность основного функционала, второе — новые возможности. Это особенно важно в периоды высокой нагрузки или при возникновении проблем.
Эта работа ещё не завершена, но мы уже понимаем, в каких случаях она сделает драконов более счастливыми.
Вопрос гибкого управления ресурсами и контроль Blast Radius вышел на первый план разработки. Сейчас мы работаем над разделением Segment-ов на два типа: Segment-core и Segment-high-risk. Разделение производится только на уровне маршрутизации запросов, код остаemся единым. Core-часть должна обеспечивать минимально необходимый набор функциональности для работы большинства клиентов, в то время как Segment-high-risk включает все второстепенные функции. Такой подход позволяет сохранять критически важную функциональность даже при проблемах с высокорисковой частью приложения, обеспечивая непрерывность бизнеса.
Попутно мы работаем над вынесением части функциональности из Монолита наружу - но это совсем другая история.

Заключение: Уроки кормления драконов
Оглядываясь назад на десятилетие развития нашего королевства, я вижу историю постоянной адаптации и эволюции. От одной фермы и одного города мы прошли путь до сложной федеративной системы независимых королевств, городов и специализированных поселений.
Каковы же основные уроки, которые мы извлекли из этого путешествия?
Первый урок: Драконы непрерывно растут. Никогда не рассчитывайте на стабильность нагрузки. Ваши клиенты будут расти как в количестве, так и в аппетитах. Проектируйте архитектуру, которая может масштабироваться в обоих направлениях. Как инженеры, постоянно задавайте себе и бизнесу вопросы типа: “А на какой поток клиентов нужно рассчитывать через год? Через два? А справится ли с этим наша текущая инфраструктура? А какой у нас запас прочности?”
Второй урок: Изоляция проблем. Создавайте системы, где проблема в одной части не влияет на всё остальное. Шарды, сегменты, микросервисы — все эти концепции помогают локализовать проблемы. Чем лучше изолированы отдельные фичи, отдельные клиенты - тем ниже вероятность того, что какая-то проблема уронит всё ваше приложение. Принцип наименьшего зла здесь помогает как никогда.
Третий урок: Разные драконы - разные потребности. Некоторые клиенты требуют особого подхода. Будьте готовы выделить специальные ресурсы для VIP-клиентов, не нарушая обслуживания остальных. Да, это будет один клиент на 10.000, на 100.000 простых клиентов. Но когда он придет, и вы, и бизнес захотите его сделать довольным.
Четвертый урок: Эволюция, а не революция. Скорее всего, у вас никогда не будет возможности отложить бизнес в сторону на квартал, чтобы решить какие-то технические вопросы. Изменение архитектуры продукта, у которого много активных клиентов - это как менять колеса на машине, которая едет по шоссе. Думайте не только о том, в какое состояние вы хотите прийти, но и как туда прийти.
Пятый урок: Универсальных решений не существует. Монолит, микросервисы, шардирование… Это всё инструменты. Нет универсального инструмента, идентифицируйте проблему, которая вам мешает, а дальше ищите варианты, как её можно решить. Где-то вы сможете воспользоваться стандартным решением, а где-то поймете, что можно пойти альтернативным путем, потому что он лучше для вас. Не бойтесь этого.
В конце концов, кормление драконов — это не просто доставка пищи. Это сложная хореография между клиентскими ожиданиями, техническими возможностями и бизнес-требованиями. Это постоянный поиск баланса между скоростью, стабильностью и масштабируемостью.
И помните: неважно, насколько сложной становится ваша архитектура, цель всегда одна - сделать драконов счастливыми, чтобы золото продолжало течь.
Об авторе
Меня зовут Анатолий Кондратьев, Engineering Manager в Wrike. За 10 лет работы я стал свидетелем всей той эволюции, что описана в этой статье.
Особое место в моей работе занимает Product Reliability — я руковожу Backend Reliability командой, задача которой — обеспечить стабильную работу приложения в разрезе Java-приложения. Масштабируемость систем — одна из моих главных профессиональных страстей. Я считаю, что способность архитектуры расти вместе с бизнесом — это не просто техническая необходимость, а настоящее искусство, требующее как инженерной точности, так и творческого подхода.
Если вам понравилась эта история о приключениях драконов и фермеров (или о масштабировании SaaS-приложения), я буду рад обсудить ваш опыт и поделиться дополнительными деталями нашего путешествия.