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

Курсы

Программирование
iOS Developer. Basic
-23%
Python Developer. Professional
-13%
Golang Developer. Professional
-17%
Python Developer. Basic
-16%
iOS Developer. Professional
-13%
C# ASP.NET Core разработчик
-18%
Unity Game Developer. Professional
-11%
React.js Developer
-12%
Android Developer. Professional
-7%
Software Architect
-12%
C++ Developer. Professional
-8%
Разработчик C#
-8%
Backend-разработчик на PHP
-8%
Архитектура и шаблоны проектирования
-12%
Программист С Разработчик на Spring Framework MS SQL Server Developer AWS для разработчиков Cloud Solution Architecture Разработчик голосовых ассистентов и чат-ботов Vue.js разработчик VOIP инженер Нереляционные базы данных Супер - интенсив по паттернам проектирования Супер-практикум по использованию и настройке GIT IoT-разработчик Advanced Fullstack JavaScript developer Супер-интенсив Azure
Инфраструктура
Мониторинг и логирование: Zabbix, Prometheus, ELK
-17%
DevOps практики и инструменты
-18%
Архитектор сетей
-21%
Инфраструктурная платформа на основе Kubernetes
-22%
Супер-интенсив «IaC Ansible»
-16%
Супер-интенсив по управлению миграциями (DBVC)
-16%
Administrator Linux. Professional
-5%
Administrator Linux.Basic
-10%
Супер-интенсив «ELK»
-10%
Базы данных Сетевой инженер AWS для разработчиков Cloud Solution Architecture Разработчик голосовых ассистентов и чат-ботов Внедрение и работа в DevSecOps Супер-практикум по работе с протоколом BGP Супер - интенсив по паттернам проектирования Супер - интенсив по Kubernetes Супер-интенсив «СУБД в высоконагруженных системах»
Специализации Курсы в разработке Подготовительные курсы
+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 комментариев
Для комментирования необходимо авторизоваться