Архитектурные решения в backend: 5 практических приёмов, которые помогают держать баланс

Страницы:  1

Ответить
 

Professor Seleznov


Всем привет, меня зовут Сергей Прощаев. Я Tech Lead и руководитель направления Java | Kotlin разработки в FinTech, а ещё преподаю на курсах разработки и архитектуры в OTUS. Сегодня хочу поговорить о том, что на собеседованиях звучит гордо, а на реальных проектах часто превращается в сложности: об архитектурных решениях в backend‑разработке. Точнее — о том, почему наши «идеальные» архитектуры превращаются в Big Ball of Mud, и как этого избежать, не впадая в другую крайность — паралич анализа и бесконечные диаграммы.
Почему «идеальная архитектура» трещит по швам
Знаете это чувство? Проекту полгода, вы начинали на чистом энтузиазме, выбрали модный стек, разбили всё на микросервисы, настроили CI/CD. А теперь каждая фича превращается в квест: нужно обновить три сервиса, согласовать контракты, разобраться с распределёнными транзакциями и понять, почему упал staging. Код стал тем самым «комком грязи», который уже не спасают ни Docker, ни Kubernetes.
Почему так происходит? Основная ловушка — мы пытаемся спроектировать финальную архитектуру в момент максимальной неопределённости. Когда требования меняются каждую неделю, бизнес сам не до конца понимает, куда пойдёт продукт, а мы уже закладываем event sourcing, CQRS и асинхронные очереди. В стартапах и быстрорастущих продуктах это особенно заметно. Бизнесу нужен быстрый MVP, а инженерная гордость подталкивает заложить «масштабируемое решение». В итоге — переусложнённая система, которую невозможно развивать без полного рефакторинга, денег на который никто не даст.
Я не раз попадал в эту ситуацию. В FinTech‑проектах, где важна надёжность, цена ошибки особенно высока. Помню, как один платёжный сервис мы с командой сразу спроектировали микросервисами: отдельно — приём платежей, отдельно — валидация, отдельно — нотификации. Всё по лучшим канонам. Через полгода выяснилось: бизнес‑процесс проведения платежа требует жёсткой консистентности, и мы мучались с распределёнными транзакциями, сагами и компенсациями.
В конце концов пришлось откатиться к модульному монолиту, где вся бизнес‑логика выполнялась в одной ACID‑транзакции, а интеграции были вынесены в адаптеры. А спустя ещё год, когда мы чётко нащупали domain boundaries, отдельные модули естественным образом стали кандидатами на выделение в самостоятельные сервисы. Но это было уже осознанное решение, а не дань моде.
Этот опыт научил меня простой мысли: хорошая архитектура не та, что предсказывает будущее, а та, что позволяет дешево менять решения. Именно об этом сегодня и расскажу.
Эволюционная архитектура: меньше предсказаний — больше адаптивности
На своих занятиях я часто показываю слайд с понятием «эмерджентная архитектура» (emergent architecture), изображенный на рисунке 1.
pic
Рис. 1 Эмерджентная архитектура в действии: надземная инфраструктура не замораживает пространство под собой, оставляя город открытым для изменений
Ключевая идея: в условиях высокой неопределённости мы не можем и не должны принимать необратимых решений раньше времени. Каждое архитектурное решение — это опцион, который мы держим открытым настолько долго, насколько это возможно. Решение становится обязательством (commitment) только когда без него уже нельзя двигаться дальше.
Это не значит, что надо писать код бездумно. Это значит — итеративно‑инкрементально уточнять архитектуру вместе с пониманием предметной области. Вместо того чтобы сразу разбивать систему на десять микросервисов, мы стартуем с модульного монолита. Жёстко разделяем код на модули, строго следим за направлением зависимостей, используем принцип разделения ответственности (SoC). А границы модулей чертим по мере накопления знаний о бизнес‑домене.
Мне очень близок подход Domain‑Driven Design в его стратегической части. Мы не пытаемся угадать все ограниченные контексты (Bounded Contexts) заранее, а ищем их «на живую»: наблюдаем, где чаще всего меняются требования, где команды начинают мешать друг другу, где возникает разное понимание одних и тех же терминов. Такие естественные швы и становятся кандидатами на границы будущих сервисов.
Важный момент: эволюционная архитектура без дисциплины превращается в хаос. Нужно осознанно управлять архитектурными решениями. Именно для этого я использую Architectural Decision Records (ADR) — лёгкие документы, в которых фиксирую контекст, рассмотренные альтернативы, принятое решение и его последствия. Отклонённые варианты тоже записываю — через полгода они часто всплывают, и коллеги понимают, почему мы тогда пошли иначе. ADR отлично работают в связке с C4-диаграммами: System Context, Container, Component — они дают картинку текущего состояния, а ADR объясняют, как мы к этому пришли.
Практические приёмы, которые помогают держать баланс
1. Начинайте с модульного монолита.
Это не откат к 2000-м. Современный модульный монолит — это хорошо структурированное приложение, в котором каждый модуль инкапсулирует свою логику, работает со своими моделями (DTO, domain models, persistence models) и коммуницирует с другими модулями через чёткие API, а не через прямые обращения к базе. Я предпочитаю раскладывать модели по DDD‑стилю: транспортные модели для контрактов, модели хранения для БД, внутренние модели предметной области. Связываю их через мапперы, не позволяя смешиваться.
2. Применяйте принцип YAGNI без фанатизма.
You Aren«t Gonna Need It — не нужно закладывать функциональность, которая не подтверждена текущими требованиями. Но при этом стоит предусматривать точки расширения. Например, если вы знаете, что через два месяца добавится второй способ нотификации, заложите интерфейс и стратегию, но не реализуйте вторую стратегию до тех пор, пока она не понадобится. Это классический компромисс между KISS и DRY — код должен оставаться простым, но не дублироваться там, где это аукнется.»
3. Используйте порты и адаптеры (гексагональную архитектуру).
Это великолепный шаблон для модульного монолита, который потом легко эволюционирует в микросервисы. Бизнес‑логика не зависит от транспорта (HTTP, Kafka) и хранилища. Когда приходит время выделить функциональность в отдельный сервис, вы просто меняете вызов метода на HTTP/gRPC, оборачиваете адаптер и не трогаете ядро. На рисунке 2 изображен пример.
pic
Рис. 2 Принцип портов и адаптеров: бизнес‑логика изолирована от инфраструктуры
Пунктирные линии здесь лишь обозначают, что адаптеры не должны напрямую влиять на ядро; основная связь — через порты.
Ядро приложения (бизнес‑логика) ничего не знает ни о HTTP, ни о базе данных, ни о Kafka. Оно общается с внешним миром через абстракции — порты (интерфейсы). Адаптеры подключаются к этим портам снаружи. Именно это позволяет:
  • легко заменить БД или транспорт, не трогая бизнес‑логику;
  • тестировать ядро изолированно;
  • при необходимости выделить модуль в отдельный микросервис, не переписывая его.
4. Документируйте архитектуру как код.
Я уже упомянул ADR. Добавьте к ним C4-диаграммы в виде PlantUML или Mermaid, которые хранятся в репозитории. Так документация остаётся живой и не устаревает уже через спринт, как многостраничные ТЗ из 2000-х.
5. Встраивайте тестируемость на уровне архитектуры.
Нефункциональные требования — не галочка для отчёта. Если вы с самого начала проектируете модули так, чтобы их можно было тестировать изолированно, имитируя границы, — вы закладываете огромный запас прочности. Это напрямую влияет на надёжность и скорость внесения изменений.
О чём молчат учебники, когда рассказывают про best practices
В учебниках часто показывают готовые схемы: вот вам Event‑Driven, вот CQRS, вот чистая луковичная архитектура. Но реальный мир грязнее. Успешные команды не исповедуют одну религию. Они эволюционируют прагматично.
Взгляните на эволюцию многих известных проектов. Netflix начинали с монолита, и только когда масштаб и отказы стали болезненными, всерьёз вложились в микросервисы — но при этом параллельно выстроили культуру Chaos Engineering и организационно перешли на DevOps‑команды. Или история с Uber: компания прошла путь от монолита к мелким микросервисам, а затем — к более крупным domain‑oriented сервисам, когда стало очевидно, что слишком мелкая нарезка создаёт невыносимую сложность. То есть они сначала приняли одно решение, потом осознали его цену и скорректировали курс. Именно эту свободу и даёт эволюционная архитектура: право передумать без катастрофы.
Популярная платформа Notion в какой‑то момент переписывала свой backend с монолита на микросервисы, но в итоге пришла к гибридной модели: часть логики осталась в монолите, часть вынесена. Потому что бизнес‑требования к скорости коллаборации не вписывались в сетевые задержки микросервисов.
Все эти кейсы объединяет одно: архитектурные решения не были высечены в камне. Они документировались, пересматривались и менялись вместе с ростом понимания.
Схема — как выглядит эволюционный путь
Давайте подведу итог схемой. Она показывает не строгий алгоритм, а скорее направление движения в типичном backend‑проекте (рис. 3).
pic
Рис. 3. Эволюция backend‑архитектуры: от монолита к микросервисам через модульный монолит
Главная идея: микросервисы — это не стартовая точка, а результат зрелости системы и команды. Начинаем с простого, усложняем ровно тогда, когда простота перестаёт справляться с нефункциональными требованиями: производительностью, надёжностью, независимостью развёртывания.
Заключение: архитектор — это стратег, а не предсказатель
Работа архитектора в backend‑разработке — это не создание идеальной диаграммы. Это способность видеть систему в динамике: предвидеть точки напряжения, откладывать необратимые решения и формировать среду, в которой команда может быстро адаптироваться.
Хорошая архитектура экономит миллионы рублей и месяцы разработки. Плохая — блокирует бизнес и убивает мотивацию команды. Модульный монолит, DDD, ADR, гексагональная архитектура — это не магические заклинания, а прагматичные инструменты в руках того, кто умеет думать категориями изменений.
Если вам близок такой взгляд и хочется разобраться глубже — приглашаю на свои бесплатные открытые уроки курса «Архитектор программного обеспечения» в OTUS. Там мы на практике посмотрим, как принимать архитектурные решения в условиях неопределённости, проектировать модульные системы и выбирать стиль, который не превратится в комок грязи через полгода.
  • 5 мая в 20:00 — «Архитектурные решения в backend-разработке». Записаться
  • 21 мая в 20:00 — «API Gateway и не только: шаги к идеальной архитектуре внешних API». Записаться
Перед глубоким погружением в архитектуру полезно честно сверить текущий уровень. Пройдите бесплатное вступительное тестирование. ➡[Пройти тестирование]
-Источник
 
Loading...
Error