Post

Эволюция архитектур: от монолита к микросервисам

Эволюция архитектур: от монолита к микросервисам

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

Монолитная архитектура (Monolith)

В начале 2000-х годов монолитная архитектура была основным выбором для большинства веб-приложений. Это объяснялось несколькими причинами:

  • Простота разработки: все компоненты системы (UI, бизнес-логика и доступ к данным) находились в одном приложении.
  • Стандартный подход в индустрии: большинство популярных платформ (Java EE, .NET) и стек доступных технологий по умолчанию предполагали разработку монолитных приложений.
  • Ограниченные требования к масштабируемости: пользовательские нагрузки большинства систем позволяли обслуживать их силами одного сервера, без сложных решений по горизонтальному масштабированию.
  • Отсутствие зрелых альтернатив: контейнеризация, брокеры сообщений и облачные сервисы либо отсутствовали, либо только начинали развиваться и не имели широкого распространения в индустрии.

Для EduSphere на начальном этапе это означало, что будет единое приложение для управления студентами, преподавателями, курсами и платежами. Все сущности будут храниться в одной базе данных. Простая схема деплоя: сборка приложения и развертывание на единственном сервере приложений (или сервлет-контейнере).

Выглядеть это могло следующим образом:

Монолитная layered архитектура EduSphere

Весь функционал системы был собран в одном развертываемом артефакте (обычно WAR или EAR). В нем компоненты пользовательского интерфейса, бизнес-логики и доступа к данным (Data Access Layer) располагались в виде слоев внутри общего кода. Такая структура известна как слоеная (layered) архитектура.

Артефакт развертывался в сервлет-контейнере (например, Apache Tomcat) или в сервере приложений (WildFly, WebLogic, IBM WebSphere, JBoss), если использовались компоненты EJB.

Взаимодействие с базой данных осуществлялось через слой DAO (Data Access Object), который напрямую выполнял SQL-запросы или работал через ORM-фреймворки (если они применялись).

Разработка и развертывание (CI/CD)

На этапе монолита процесс разработки и развертывания выглядел следующим образом:

  • Все команды работали с единой кодовой базой в общем репозитории (Single Code Repo).
  • Код собирался с помощью инструментов сборки (Ant, Ivy, позднее Maven или Gradle).
  • Для автоматизации сборки и деплоя использовались системы CI/CD — чаще всего Hudson или Jenkins.
  • Артефакт сборки (обычно EAR или WAR) включал в себя весь функционал приложения: UI, бизнес-логику и доступ к данным.
  • После сборки приложение развртывалось в сервлет-контейнере (Apache Tomcat) или сервере приложений (JBoss).

Все приложение использовало единую базу данных.

Скелетон приложения мог быть таким, например:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
EduSphere/
├── src/                           (Исходный код: UI, бизнес-логика, DAO и модели) 
│   ├── com/edusphere/ui/          (JSP, Servlets)
│   ├── com/edusphere/business/    (Бизнес-логика)
│   ├── com/edusphere/dao/         (Data Access Objects, JDBC)
│   └── com/edusphere/model/       (DTO, сущности)
├── web/                           (Фронтенд-часть и конфигурации сервера) 
│   ├── WEB-INF/                   (Java EE/Spring конфиги)
│   │   ├── web.xml
│   │   ├── applicationContext.xml (если подключали Spring)
│   └── static/                    (CSS, JS, изображения)
├── lib/                           (сторонние библиотеки, JAR'ники)
├── build.xml                      (Ant build script)
├── README.txt
└── db/                            (SQL-скрипты для создания схемы и начальных данных)
    ├── schema.sql
    └── seed_data.sql

Преимущества монолита

Рассмотрим ключевые преимущества такого подхода.

  • Простая разработка и деплой для небольших команд и приложений. Монолитная архитектура подразумевает единую кодовую базу и общее приложение. Это упрощает процесс проектирования, так как не требуется продумывать механизмы взаимодействия между отдельными сервисами или сложную инфраструктуру. Для небольших команд это означает возможность быстро разрабатывать и развертывать новые функции без необходимости координировать изменения между множеством сервисов.
  • Меньше кросс-функциональных задач (security, monitoring) на старте. В монолите большинство кросс-функциональных аспектов (например, безопасность, мониторинг, логирование) реализуются централизованно. Разработчикам не нужно заботиться о том, как согласованно применять эти аспекты во множестве отдельных сервисов, как это требуется в микросервисной архитектуре. Это упрощает старт проекта и снижает начальные накладные расходы.
  • Лучшая производительность: нет сетевых задержек между компонентами. В монолитных приложениях все модули взаимодействуют между собой через вызовы внутри процесса (in-process calls), которые значительно быстрее сетевых запросов. Это особенно важно для операций, требующих высокой скорости отклика или большой пропускной способности. Отсутствие сетевых задержек и расходов на сериализацию данных позволяет достигать более высокой производительности по сравнению с распределенными системами.

Ограничения монолита

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

  • Сложность внедрения новых технологий. Все компоненты монолитного приложения связаны общей кодовой базой и общим циклом сборки. Внедрение новой технологии в одной части приложения (например, переход на новый фреймворк или СУБД) часто требует изменений во всех слоях и модулях. Это увеличивает сложность миграции и может привести к непредвиденным ошибкам в других частях системы.
  • Ограниченная гибкость Монолитная архитектура плохо подходит для команд, которые хотят работать независимо друг от друга. Любые изменения в одной части системы требуют пересмотра и тестирования всего приложения. Это ограничивает возможности команды быстро адаптироваться к меняющимся требованиям бизнеса.
  • Единая кодовая база Большие монолиты со временем становятся трудно поддерживаемыми. Из-за большого количества взаимосвязей, изменения в одном модуле могут повлиять на другие, что увеличивает вероятность ошибок и усложняет тестирование. Работа с общей кодовой базой также затрудняет параллельную разработку несколькими командами.
  • Отсутствие устойчивости к сбоям (Fault Tolerance) Если один модуль монолитного приложения выходит из строя (например, из-за ошибки в коде или проблем с памятью), это может привести к сбою всего приложения. Монолитные системы обычно не предусматривают механизмов изоляции сбоев между модулями.
  • Любое обновление требует полного деплоя Даже небольшие изменения, затрагивающие только одну функциональность, требуют пересборки и полного развертывания всего приложения. Это увеличивает время вывода обновлений в продакшн (deployment time) и повышает риск внесения непреднамеренных ошибок.

Монолиты сегодня

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

Со временем появилось несколько подходов к структурированию монолитных приложений:

  • Single-Process Monolith Классический монолит: все слои и компоненты запускаются в рамках одного процесса. Минимальная сложность, но максимальная связанность.

  • Modular Monolith Приложение физически единое, но логически разделено на отдельные модули с чткими границами ответственности. Это позволяет ограничить связанность и облегчить сопровождение кода.

  • Distributed Monolith Формально приложение разделено на несколько развертываемых компонентов или сервисов, но в реальности между ними сохраняется сильная связанность (shared-базы данных, синхронные вызовы), из-за чего оно унаследовало многие проблемы классического монолита.

Service-Oriented Architecture (SOA)

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

Чтобы справиться с новыми требованиями, архитектура программных систем эволюционировала в сторону сервис-ориентированной модели (SOA).

SOA — это архитектурный подход, в котором система организована как набор слабо связанных сервисов. Каждый сервис реализует отдельную бизнес-функцию (например, управление студентами, курсы или платжную систему). Взаимодействие между сервисами обычно происходит через интеграционную шину (Enterprise Service Bus, ESB), которая обеспечивает маршрутизацию, преобразование (трансформацию) сообщений, безопасность, логирование и контроль доступа.

Сервис-ориентированная архитектура EduSphere

Этот подход стал логичным следующим шагом после монолита и получил широкое распространение в 2000-е годы, особенно в больших корпоративных приложениях.

Причины перехода к SOA

Переход на сервис-ориентированную архитектуру был обусловлен несколькими ключевыми факторами:

  • Рост функциональности. Появление новых модулей (например, в случае с EduSphere - это могли бы быть онлайн-курсы, аналитика, интеграции с внешними системами) сделало монолит слишком громоздким и сложно расширяемым.
  • Необходимость независимого развития команд. Разработчики, отвечающие за разные области (например, студенты, курсы, платежи), все чаще сталкивались с ограничениями общей кодовой базы. Им требовалась возможность развивать функциональность без постоянной координации с другими командами.
  • Упрощение масштабирования. Возникла необходимость масштабировать отдельные компоненты системы в зависимости от их нагрузки. Например, сервис оплаты мог требовать большей вычислительной мощности по сравнению с управлением курсами.
  • Повышение отказоустойчивости. В случае сбоя одного модуля система должна продолжать функционировать, что труднодостижимо в монолите.
  • Возможность повторного использования сервисов. Бизнес-функции (например, платежи или уведомления) должны быть доступны для разных частей системы или даже внешних клиентов.

Идеальный vs реальный SOA

В идеальном варианте SOA все клиенты — это отдельные приложения (например, Student Portal, Teacher Portal, Admin Panel), которые могут развертываться независимо друг от друга. ESB представляет собой отдельный компонент уровня middleware и развертывается как самостоятельный процесс или кластер. Каждый сервис (StudentService, CourseService и т. д.) по замыслу также должен быть независимым и развертываться отдельно.

Однако в реальных реализациях SOA сервисы нередко группировались в один процесс или приложение — это зависело от бюджета, опыта команды и технических ограничений. Несмотря на то, что каждый сервис имел четкие логические границы, на практике часто все еще существовали крупные сборки, в которые входили сразу несколько сервисов (например, веб-интерфейс и связанные с ним модули на одном сервере).

Типичный стек технологий SOA

Обычный технологический стек того времени включал:

  • Enterprise Service Bus (ESB): IBM WebSphere ESB, Oracle Service Bus (OSB), Apache ServiceMix, Mule ESB.
  • Серверы приложений: IBM WebSphere Application Server, Oracle WebLogic Server, JBoss Application Server, Apache Tomcat (иногда использовался для менее сложных сервисов).
  • Фреймворки и экосистемы: Spring Framework (часто для сервисов), Java EE (EJB, JAX-WS/JAX-RS).
  • Коммуникации между сервисами: SOAP (основной стандарт в SOA), REST (начал использоваться ближе к 2010-м).
  • База данных: Oracle Database, IBM DB2, MS SQL Server.
  • CI/CD: Инструменты автоматизации сборки и деплоя чаще всего включали Jenkins, TeamCity или Bamboo. Однако во многих проектах сборка и развертывание оставались частично ручными или полуавтоматизированными.

Статьи серии

This post is licensed under CC BY 4.0 by the author.