Логотип Workflow

Article

Updated at:

Stage 4: Multi-module Projects

Этап 4 - Многомодульные проекты: архитектура больших Java-систем

Один модуль удобен, пока приложение маленькое. Но в большом backend-проекте быстро появляются DTO для API, бизнес-логика, работа с базой данных, веб-контроллеры, общие утилиты и иногда сгенерированные клиенты. Если все это лежит в одном модуле, проект становится трудно читать, тестировать и переиспользовать.

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

Этап 4 - Многомодульные проекты: архитектура больших Java-систем

Когда один модуль уже мешает

Представь сервис заказов. В нем есть HTTP-контроллеры, бизнес-правила, JPA-репозитории, DTO и общие классы. Если все находится в одном модуле, разработчик может случайно вызвать controller из service или использовать web-класс внутри repository. Код начинает зависеть от всего подряд.

Модули помогают явно показать границы. web отвечает за HTTP. service отвечает за бизнес-логику. repository отвечает за работу с базой. api может хранить DTO и контракты. common хранит действительно общие классы, но туда нельзя складывать все подряд.

В Maven корневой pom.xml часто имеет тип упаковки pom и секцию <modules>. В этой секции перечисляют дочерние модули. Корневой файл также может задавать общие свойства, версии зависимостей и настройки плагинов, которые наследуют дочерние модули.

Aggregator - это проект, который перечисляет модули и запускает их сборку вместе. Parent - это проект, от которого модули наследуют настройки. В реальных Maven-проектах один корневой POM часто выполняет обе роли, но полезно понимать разницу.

Зависимости между модулями должны идти в одну сторону. web может зависеть от service, а service может зависеть от repository. Но repository не должен зависеть от web. Если появляется цикл, границы модулей выбраны неправильно.

Последовательность шагов

  1. Корневой проект объявляет список модулей.
  2. Каждый модуль описывает свои зависимости.
  3. Общие версии и настройки выносятся в корневой файл.
  4. Maven или Gradle определяет порядок сборки.
  5. Сначала собираются нижние модули, затем модули, которые от них зависят.

Главная картинка такая: верхний слой вызывает нижний, но нижний слой не знает о верхнем.

Конкретный пример

project
|-- api
|-- service
|-- common
|-- repository
`-- web

Этот пример показывает не папки ради папок, а разделение ответственности. Если нужно изменить HTTP endpoint, ты идешь в web. Если меняется бизнес-правило, ты идешь в service. Если меняется SQL или JPA-запрос, ты идешь в repository. Если несколько модулей используют один класс, он может лежать в common.

Пример корневого Maven-файла:

<project>
  <modelVersion>4.0.0</modelVersion>
  <packaging>pom</packaging>

  <modules>
    <module>api</module>
    <module>service</module>
    <module>repository</module>
    <module>web</module>
  </modules>
</project>

Полезная таблица

ПонятиеЗначение
ParentРодительский проект, от которого модули наследуют настройки.
AggregatorКорневой проект, который перечисляет модули и запускает общую сборку.
Module dependencyЗависимость одного модуля от классов или артефакта другого модуля.
Reactor buildMaven-сборка связанных модулей в правильном порядке.

Где это встречается в работе

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

Частые ошибки

  • Делать модуль для каждой маленькой папки.
  • Создавать циклические зависимости между модулями.
  • Складывать весь общий код в common, пока он не превращается в свалку.
  • Путать parent и aggregator и не понимать, откуда модуль наследует настройки.

Чеклист понимания

  • Я могу объяснить, зачем проект делят на модули.
  • Я понимаю, почему зависимости должны идти в одну сторону.
  • Я могу прочитать секцию <modules> в Maven.
  • Я понимаю, какую роль выполняют web, service и repository.

Вопросы для самопроверки

  1. Почему repository не должен зависеть от web?
  2. Чем parent отличается от aggregator?
  3. Когда модуль common становится проблемой?

Практика перед следующим уроком

Нарисуйте проект из четырех модулей: web, service, repository и common. Проведите стрелки только туда, куда код действительно может зависеть. Если у вас появилась стрелка от repository к web, объясните, какое архитектурное правило нарушено.

Авторизуйтесь чтоб пройти тесты

Practice

Интерактивная практика

Выполните задания и сразу проверьте ответ.