Структура проекта — это то, как мы раскладываем классы по папочкам. Хорошая структура помогает не только ориентироваться в проекте, но и писать более качественный код.
Сейчас покажу, как это работает.
Разделение по слоям
Начнём с структуры, которая встречается в большинстве туториалов и пет-проектах начинающих:
📂 controller
— UserController, TicketController
📂 service
— UserService, TicketService
📂 repository
— UserRepository, TicketRepository
Чтобы классы могли использовать друг друга, все классы и методы должны быть
public. В такой структуре естественным путём повышается связность. Если в
UserService хочется узнать номер билета, то самое простое — добавить TicketRepository и вызвать нужный метод. Всё связано со всем. Поменяешь в одном месте — сломается в другом. В пет-проекте с этим можно справиться, но для коммерческих проектов такая структура не подходит
Разделение по функциям
Складываем в один пекедж всё, связанное с какой-то сущностью. Оставляем 1-2 класса с модификатором
public, остальным даём дефолтный модификатор доступа: 📂 user
— UserController, UserService, UserRepository
📂 ticket
— TicketController, TicketService, TicketRepository
📂 export
— ExportService, ExcelFormatter
Дефолтный модификатор ограничивает доступ между пэкеджами. Если
UserService хочет сформировать отчёт по пользователям, он вынужден идти через ExportService, потому что ExcelFormatter ему не виден.✅ Связность классов снижается, упрощается поддержка и тестирование
😐 Каждый класс решает не бизнес-задачу, а инфраструктурную.
UserRepository — точка доступа к таблице users. UserService — класс по работе с классом User. Классы становятся огромными😐 Высокая связность между бизнес-кейсами. Появляются десятки универсальных методов, которые "переиспользуются" в бизнес-сценариях. Например, создание и редактирование пользователя часто делают через один метод. Меняем одно — неизбежно задеваем похожие сценарии.
Разделение по бизнес-кейсам
Складываем в один пекедж все классы, связанные с бизнес-процессом. Большинство классов стоит с
default модификатором и недоступна за пределами пэкеджа: 📂 newUser
— NewUserController, NewUserService, UserRepository
📂 buyTicket
— BuyTicketController, BuyTicketService, TicketRepository
📂 refundTicket — …
📂 export — …
Количество классов увеличивается, но они становятся меньше и более изолированными. Связность между бизнес-сценариями максимально снижается.
Итого: чёткая структура проекта и модификаторы доступа снижают связность между компонентами на уровне компиляции.
Однако очень мало проектов используют эту практику. Не потому что разработчики плохие, а потому что на большинстве проектов этот подход не сработает. Почему так получается и кто виноват — расскажу в следующем посте:)