Как структурировать проекты на Spring/Spring Boot? | OTUS
⚡ Подписка на курсы OTUS!
Интенсивная прокачка навыков для IT-специалистов!
Подробнее

Курсы

Программирование
Python Developer. Professional
-3%
Разработчик на Spring Framework
-5%
iOS Developer. Professional
-8%
Golang Developer. Professional
-6%
Agile Project Manager
-5%
C# ASP.NET Core разработчик
-6%
Android Developer. Basic
-10%
React.js Developer
-4%
MS SQL Server Developer
-8%
Scala-разработчик
-8%
Java Developer. Basic
-8%
Разработчик IoT
-13%
PostgreSQL Backend-разработчик на PHP Алгоритмы и структуры данных Разработчик программных роботов (RPA) на базе UiPath и PIX Unity Game Developer. Basic Разработчик голосовых ассистентов и чат-ботов Vue.js разработчик VOIP инженер NoSQL Супер-практикум по использованию и настройке GIT Symfony Framework iOS Developer. Basic Супер-интенсив «СУБД в высоконагруженных системах» Супер-интенсив "Tarantool"
Инфраструктура
DevOps практики и инструменты
-12%
Network engineer. Basic
-10%
Network engineer
-4%
Экcпресс-курс «ELK»
-10%
Инфраструктурная платформа на основе Kubernetes
-6%
Экспресс-курс по управлению миграциями (DBVC)
-10%
Мониторинг и логирование: Zabbix, Prometheus, ELK Дизайн сетей ЦОД
-13%
PostgreSQL Administrator Linux. Professional Разработчик программных роботов (RPA) на базе UiPath и PIX Reverse-Engineering. Professional Внедрение и работа в DevSecOps Administrator Linux. Advanced Infrastructure as a code in Ansible Супер - интенсив по паттернам проектирования Супер - интенсив по Kubernetes Экспресс-курс «IaC Ansible»
Специализации Курсы в разработке Подготовительные курсы
+7 499 938-92-02

Как структурировать проекты на Spring/Spring Boot?

Spring_Deep_7.2-5020-ba226e.png

В данном тексте мы попробуем разобраться в самой спорной и холиварной теме – как разбивать на модули и как расположить файлы в проекте на Java и Spring.

Представим, что мы будем структурировать большой проект, который уже существует. Так как такие большие проекты обычно пишутся большой командой и защищены NDA, то за неимением такого мы возьмём небольшой проект и будем представлять, что он очень большой!

Итак, теперь мы имеем следующую структуру папок и файлов:

.
├── application/
│   ├── src/
│   │   ├── main/
│   │   │   ├── java/
│   │   │   │   └── ru/example/
│   │   │   │       ├── controller/
│   │   │   │       │   ├── PersonController.java
│   │   │   │       │   └── OrderController.java
│   │   │   │       └── Main.java
│   │   │   └── resources/
│   │   │       └── application.properties
│   │   └── test/
│   │       └── ...
│   └── pom.xml
├── database/
│   ├── src/
│   │   └── main/
│   │       └── java/
│   │           └── ru/example/dao/
│   │               └── impl/
│   │                   ├── PersonDaoJdbc.java
│   │                   ├── OrderDaoJdbc.java
│   │                   ├── PersonDao.java
│   │                   └── OrderDao.java
│   └── pom.xml
├── model/
│   ├── src/
│   │   └── main/
│   │       └── java/
│   │           └── ru/example/model/
│   │               ├── Person.java
│   │               └── Order.java
│   └── pom.xml
├── service/
│   ├── src/
│   │   ├── main/
│   │   │   └── java/
│   │   │       └── ru/example/service/
│   │   │           ├── impl/
│   │   │           │   ├── PersonServiceImpl.java
│   │   │           │   └── OrderService.java
│   │   │           ├── PersonService.java    
│   │   │           └── OrderService.java
│   │   └── test/
│   │       └── ...
│   └── pom.xml
└── pom.xml

Возможно, вы узнали структуру вашего проекта!

Итак, начнём

Давайте попробуем упростить структуру с точки зрения современных практик и возможностей Spring Boot. Для начала посмотрим на разбиение на Maven-модули по слоям приложения. Существует такое понятие, как связность компонента (cohesion, не путать со связАнностью компонент — coupling).

Связности бывают разные: • случайная связность; • коммуникационная связность; • функциональная связность; • логическая связность; • процедурная связность; • последовательностная связность; • временнАя связность.

На самое деле, это — не лучшее деление для большого проекта. Чуть попозже мы поговорим про функциональную связность.

Существует несколько аргументов «за» такого деления: • можно на уровне зависимости Maven-модулей разделить архитектурные слои; • можно очень просто контролировать зависимости каждого слоя и используемые фреймворки.

Тем не менее существует несколько недостатков данного разделения: • собственно, функциональная связанность компонент; • большая сложность в дальнейшем разделить на микросервисы (они требуют функциональной связанности); • если вы используете Spring или Spring Boot, то начинаются проблемы с автоконфигурацией (Main -класс в одном модуле, а тестируемый класс – в другом); • да, и самое ужасное – application.properties находится в одном модуле, а использоваться могут в другом.

Объединим в один модуль. Итак, теперь мы имеем следующую структуру папок и файлов:

.
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── ru/example/
│   │   │       ├── controller/
│   │   │       │   ├── PersonController.java
│   │   │       │   └── OrderController.java
│   │   │       ├── service/
│   │   │       │   ├── impl/
│   │   │       │   │   ├── PersonServiceImpl.java
│   │   │       │   │   └── OrderService.java
│   │   │       │   ├── PersonService.java    
│   │   │       │   └── OrderService.java   
│   │   │       ├── model/
│   │   │       │   ├── Person.java
│   │   │       │   └── Order.java      
│   │   │       ├── dao/
│   │   │       │   ├── impl/
│   │   │       │   │   ├── PersonDaoJdbc.java
│   │   │       │   │   └── OrderDaoJdbc.java
│   │   │       │   ├── PersonDao.java
│   │   │       │   └── OrderDao.java
│   │   │       └── Application.java
│   │   └── resources/
│   │       └── application.properties
│   └── test/
│       └── ...
└── pom.xml

Обратим внимание, что каждый сервис покрыт отдельным интерфейсом.

Существует несколько аргументов «за» покрытие интерфейсами всех сервисов. Попробуем отойти от некоторых принципов в пользу более сжатого кода: • задать «контракт» для сервиса и соблюсти Dependency Inversion Principle. Собственно, с этим никак нельзя поспорить)). Но в современных средах разработки выделение интерфейса состоит из нажатия комбинации клавиш на клавиатуре. Эту операцию можно успеть сделать всегда, притом, что большинство интерфейсов всегда будут иметь только одного наследника; • добавить возможность создания JDK-proxy вместо CGLib-proxy при использовании AOP или некоторых проектов Spring. С этим тоже не поспоришь)). По скорости CGLib-proxy не хуже JDK dynamic proxy, правда, создаются чуть медленнее. Скорость поднятия контекста приложения – сомнительный параметр для оптимизации; • иметь возможность мокирования интерфейсов для написания Unit-тестов. А вот это не аргумент! C Mockito можно создать моки всех методов без наличия интерфейса.

Уберём ненужные интерфейсы.

.
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── ru/example/
│   │   │       ├── controller/
│   │   │       │   ├── PersonController.java
│   │   │       │   └── OrderController.java
│   │   │       ├── service/
│   │   │       │   ├── PersonService.java    
│   │   │       │   └── OrderService.java   
│   │   │       ├── model/
│   │   │       │   ├── Person.java
│   │   │       │   └── Order.java      
│   │   │       ├── dao/
│   │   │       │   ├── PersonDao.java
│   │   │       │   └── OrderDao.java
│   │   │       └── Application.java
│   │   └── resources/
│   │       └── application.properties
│   └── test/
│       └── ...
└── pom.xml

И, наконец, переструктурируем в функциональную связность:

 .
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── ru/example/
│   │   │       ├── person/
│   │   │       │   ├── Person.java
│   │   │       │   ├── PersonDao.java
│   │   │       │   ├── PersonController.java
│   │   │       │   └── PersonService.java    
│   │   │       ├── service/
│   │   │       │   ├── Order.java      
│   │   │       │   ├── OrderDao.java
│   │   │       │   ├── OrderController.java
│   │   │       │   └── OrderService.java   
│   │   │       └── Main.java
│   │   └── resources/
│   │       └── application.properties
│   └── test/
│       └── ...
└── pom.xml

Вот и всё. Сравните теперь с исходным проектом!

Не пропустите новые полезные статьи!

Спасибо за подписку!

Мы отправили вам письмо для подтверждения вашего email.
С уважением, OTUS!

Автор
0 комментариев
Для комментирования необходимо авторизоваться