4 подхода к проектированию и организации кода

4 подхода к проектированию и организации кода

Поговорим про несколько подходов проектирование и организации кода.

Перед тем, как начнем рассматривать следующие подходы, важно сказать, что мы строим книжный онлайн-магазин и мы реализуем возможность просмотра клиентом своих заказов.
А также имеем следующие Java-типы

  • OrdersController: веб-контроллер, иногда что-то вроде MVC-контроллера из Spring, обрабатывающего запросы из Веб.
  • OrdersService: интерфейс, определяющий "бизнес-логику", связанную с заказами.
  • OrdersServiceImpl: реализация службы заказов.
  • OrdersRepository: интерфейс, определяющий порядок доступа к информации о заказах в хранилище.
  • JdbcOrdersRepository: реализация интерфейса хранилища.

Упаковка по уровням

Это организация традиционной многоуровневой архитектуры, в которой код разделяется по функциональному признаку.

Плюсы: отлично подходит для начального этапа запуска проекта или продукта.

Минусы: с ростом масштаба и сложности ПО трёх слоев оказывается недостаточно и приходится задумывать о более дробной органищации.

Подробнее о многоуровневой архитектуре можно прочитать в статье Мартина Фаулера - Presentation Domain Data Layering
https://martinfowler.com/bliki/PresentationDomainDataLayering.html

Упаковка по особенностям

Это разделение по вертикали, основанное на объединении связанных особенностей, предметных понятий и общих корней.

Как видно из рисунка все типы помещаются в один Java-пакет, с именем отражающим идею группировки. И теперь верхнеуровневая структура кричит о предметной области. В отличии от предыдущего подхода.

Порты и адаптеры

Такие базы кода, где мы отделяем предметный/прикладной код от  технических деталей реализации таких как фреймворки и БД. Делим на "внутренняя"(предметная) и "внешняя"(инфраструктура)

Пакет org.rogaikopyta.bookstore.domain в этой версии "внутренний", а другие пакеты "внешние".  Как вы можете заметить класс OrdersRepository переименован в Orders, потому что это правило предметно-ориентированного проектирования, которое требует всему, что находится "вниутри", давать простые имена из словаря "универсального предметного языка"(Ubiquoties language). Так в предметных дискуссиях мы говорим "заказы", а не "хранилища заказов".

Упаковка по компонентам

Целью этого гибридный подхода - упаковать все обязанности, связанные с одним крупным компонентом, в единный  Java-пакет.

На рисунке показано, как мог бы выглядить вариант "просмотр заказов". Также можно прочитать как дядюшка Боб дает определение компонента:

Компоненты - это единицы развертывания. Они представляют наименьшие сущности, которые можно развертывать в составе системы. В Java - это jar-файлы.

А также есть определение компоненты взятое из описание модели C4 программной архитектуры

Группа функциональных возможностей, находящихся за общим чистым интерфейсом, которые постоянно находятся внутри среды выполнения, такой как приложение.

https://ru.wikipedia.org/wiki/Модель_C4

В итоге, если отобразить все 4 архитектурных стиля, то  получим

Все четыре архитектурных стиля

А теперь давайте представим, что мы слишком свободно и часто используем модификатор доступа public как в Java. Почему возникла такая идея? Да потому что довольно часто можно увидеть примеры кода и в книгах, руководствах и открытых фреймворках на GitHub. И что же за этим всем последует, а то что мы отказываемся от принципа инкапсуляции и делаем все наши типы общедоступными. Как результат это даёт права любому написать код с реализацией конкретного класса, нарушающий используемый архитектурный стиль. Если отобразить это на UML диаграмме, то получится:

Все четыре архитектурных стиля идентичны

И теперь в контрасте с рисунком выше, давайте вернем пакеты и отметим те типы(сделав их бледнее на диаграмме), которым можно дать более ограничиваюший модификатор. Риунок показан ниже:

Если диваться слева направо, в подходе "по уровням", интерфейсы OrdersService и  OrdersRepository должны быть объявлены общедоступными, потому что имеют входящие зависимости от классов, находящихся за пределами пакета, в котором объявлены эти интерфейсы. А классы же OrdersServiceImpl и JdbcOrdersRepository, наоборот могут иметь ограниченную видимость.
В подходе по "особенностям" единственной точкой входа в пакет является OrdersController, поэтому всем остальным типам можно ограничить доступ рамками пакета. Информацию о заказе можно получить только через контроллер.
В походе "порты и адаптеры" интерфейсы OrdersService и Orders имеют входящие зависимости из других пакетов, поэтому они общедоступные.           И последний подход "по компонентам" интерфейс OrdersComponent имеет входящую зависимость от контроллера, но доступ ко всему остальному можно ограничить рамками пакета.

Чем меньше общедоступных типов, тем меньше число потенциальных зависимостей.

P.S. конечно, существуют и другие способы раздедения зависимостей в исходном коде, такие как Java 9 модульность, OSGI и прочие.

Заключение

Любые, даже самые лучшие дизайнерские намерения можно уничтожить в мгновении ока, если не учитывать тонкости стратегии реализации.
P.P.S. информация перенесена из книги Чистая Архитектура - @Роберта Мартина(Clean Architecture - @Robert Marting/Uncle Bob)