Каковы преимущества и сложности многомодульных проектов (Maven/Gradle)?

Ответ

Многомодульный проект — это сборка, разделённая на логически связанные модули, каждый из которых компилируется в отдельный артефакт (JAR).

Преимущества:

  • Чёткое разделение ответственности: Модуль api содержит DTO и интерфейсы, service — бизнес-логику, persistence — доступ к данным.
  • Изоляция изменений: Изменения в одном модуле не требуют пересборки всего проекта.
  • Повторное использование: Модули (например, core-utils) можно подключать в разных проектах.
  • Управление зависимостями: Зависимости объявляются только в нужных модулях, что уменьшает итоговый размер артефактов.

Структура и настройка (Maven):

<!-- Родительский pom.xml -->
<project>
    <groupId>com.example</groupId>
    <artifactId>parent-app</artifactId>
    <packaging>pom</packaging>
    <modules>
        <module>core-module</module>
        <module>web-module</module>
    </modules>
</project>

<!-- pom.xml модуля web-module -->
<project>
    <parent>
        <artifactId>parent-app</artifactId>
        <groupId>com.example</groupId>
    </parent>
    <artifactId>web-module</artifactId>
    <dependencies>
        <!-- Зависимость от другого модуля -->
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>core-module</artifactId>
            <version>${project.version}</version>
        </dependency>
    </dependencies>
</project>

Сложности и их решение:

  1. Циклические зависимости: Запрещены. Решение — рефакторинг, вынесение общих классов в новый модуль.
  2. Управление версиями: Все модули обычно используют одну версию (управляется через <version> в родительском POM).
  3. Долгая сборка: Решение — использование инкрементальной сборки в Gradle или профилей Maven для сборки только изменённых модулей.
  4. Настройка CI/CD: Необходимо настраивать pipeline так, чтобы при изменении в модуле пересобирались только зависимые модули.

Ответ 18+ 🔞

А, многомодульный проект, говоришь? Ну это когда твой код, как пьяная компания в подъезде — все в одной кучке, но каждый орёт про своё и норовит в соседнюю дверь насс... то есть, в соседний модуль залезть.

Ну и зачем этот цирк?

  • Разделили, чтобы не запутаться: Вот смотри, модуль api — это тот трезвый чувак, который только договора (DTO) и бумажки (интерфейсы) раздаёт. Модуль service — это уже работяги, которые реально пашут (бизнес-логика). А persistence — это дворник, который всё это добро в базу выносит и обратно притаскивает. Порядок, блядь!
  • Чтоб не пересобирать всю вселенную: Починил ты дворника — и не надо заново красить весь подъезд. Только его каморку.
  • Чтоб таскать с собой по проектам: Сделал ты раз полезную утилиту (core-utils), и теперь её, как бутылку, в любой проект можно принести. Удобно, ёпта.
  • Чтоб не тянуть лишний хлам: Зачем веб-модулю знать про какую-то ебучую библиотеку для работы с квантовой базой данных, если она только в модуле persistence нужна? Вот именно, нихуя не зачем.

Как это сколхозить на Maven: Родительский pom.xml — это как паспортный стол на весь этот район.

<project>
    <groupId>com.example</groupId>
    <artifactId>parent-app</artifactId>
    <packaging>pom</packaging> <!-- Смотри, не JAR, а POM! Как начальник, он сам не работает, только другими рулит -->
    <modules>
        <module>core-module</module> <!-- Вот эти два оболтуса у него на районе -->
        <module>web-module</module>
    </modules>
</project>

А вот web-module уже просит у родителя денег на пиво, то есть подключает соседа.

<project>
    <parent> <!-- Папа, дай! -->
        <artifactId>parent-app</artifactId>
        <groupId>com.example</groupId>
    </parent>
    <artifactId>web-module</artifactId>
    <dependencies>
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>core-module</artifactId> <!-- Сосед, у которого пиво -->
            <version>${project.version}</version> <!-- Версия общая, чтоб не ссориться -->
        </dependency>
    </dependencies>
</project>

А где же подводные, блядь, грабли?

  1. Циклические зависимости: Это когда api тычет пальцем в service, service орёт на persistence, а persistence в итоге приходит жаловаться обратно в api. Пиздец, замкнутый круг, как в дурке. Решение — вынести их общие игрушки в отдельную песочницу (новый модуль) и пусть там сами разбираются.
  2. Версии: Все должны жить по одной версии, как в казарме. А то будет: «Я на версии 1.0!», «А я уже 2.1!». Не, ребята, либо все 1.0, либо все 2.1. Родительский POM рулит этим шабашем.
  3. Сборка на час: Если модулей овердохуища, собираться будет, пока чайник три раза вскипит. Решение — умные системы вроде Gradle, которые пересобирают только то, что поменялось. Или в Maven профилями хитрить.
  4. CI/CD, ёпта: Настроить pipeline нужно так, чтобы если дворник (persistence) подметёл по-новому, то пересобирались только он и те, кто от него зависит (работяги service), а не весь городской квартал. А то ресурсов на всё не напасёшься.