Стратегии организации кода при построении программного обеспечения

Статья охватывает виды архитектурных шаблонов, применение их в процессе организации кода при разработке программного обеспечения. Уделяется внимание выбора чистой архитектуры. Раскрываются способы внедрения и вопросы целесообразности использования различных стратегий организации кода.

Аннотация статьи
дизайн программного кода
гексагональная архитектура
структуры организации кода
чистая архитектура
порты и адаптеры
упаковка по уровням
упаковка по функциям
упаковка по компонентам
Ключевые слова

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

Существует 4 базовых стратегий организации кода [1, с. 288-304]:

1. Традиционный и наиболее распространенный подход – это горизонтальная слоеная архитектура «Упаковка по уровням», представленная на рис.1, где код обычно разбивается по пакетам в 3 слоя. Первый пакет содержит контроллер, перехватывающий входной запрос от пользователя и далее транслирующий его на следующий слой, бизнес-логики, тогда как третий пакет необходим для работы с базой данных.

Рис. 1. Упаковка по уровням

Рассмотренная стратегия хорошо подходит для начальной фазы разработки, где разработчики могут не иметь высокой квалификации во внедрении других шаблонов. Однако архитектура с таким набором компонентов, как контроллер, сервис, хранилище (controller, service, repository) не отражает бизнес-домен и не следует «Кричащей архитектуре» (когда кто-то смотрит на каталог верхнего уровня проекта, должно быть очевидно, о чем идет речь в проекте, согласно бизнес задачам). Кроме того, контроллер может импортировать напрямую репозиторий, минуя сервис слой, образуется «Нестрогая многоуровневая архитектура», это приводит к усложнению поддержки кода из-за отсутствия слоя бизнес-логики.

2. Следующий подход для организации кода – это вертикальное разбиение проекта по задачам «Упаковка по функциям», отображенный на рис. 2. Абстракции слоев в виде контроллеров, сервисов и репозиториев исчезают, создается один пакет, со всеми необходимыми классами, которые сгруппированы относительно доменной модели, в данном случае предполагаемые заказы (orders). Здесь предусмотрена минимизация связи между срезами и максимизация связи в срезе [2].

Рис. 2. Упаковка по функциям

Преимуществом использования вертикального среза, является быстрый поиск кода, так все классы сгруппированы в одном пакете. Этот подход уже следует принципу «Кричащей архитектуре». Однако возрастает вероятность дублирования технических функций между срезами, так как теперь пакеты разбиты не по техническим слоям, а по бизнес-задачам.

3. Следующая стратегия, является «Порты и Адаптеры» или гексагональная архитектура, рис. 3, в которой центром выступает бизнес-логика, отделенная от технических агентов в виде фреймворков и баз данных. Здесь выделена отдельная сущность Orders, вместо OrdersRepository, приходящая из мира предметно-ориентированного проектирования, требующего давать названия из «универсального предметного языка». Стоит отметить, что домен не имеет никаких зависимостей от внешних секторов системы, контроллер и база данных, являются внешними относительно домен сущности, что отмечено связями между компонентами. Количество слоев может быть увеличено, например, путем разделения service (бизнес-правила приложения) и domain (бизнес правила предприятия) по разным пакетам [4].

Рис. 3. Порты и адаптеры

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

4. Еще одна стратегия «Упаковка по компонентам», представленная на рис.4, является гибридной моделью приведенных ранее подходов, в которой существует веб-слой и уровень в виде центрального компонента, охватывающего бизнес логику, и взаимодействие с базой данных.

Рис. 4. Упаковка по компонентам

«Пакет по компонентам» объединяет компоненты, таким образом контроллер не имеют возможности напрямую вызвать репозиторий.

Для внедрения каждого из этих подходов необходимо следовать свойствам инкапсуляции и применять общедоступные идентификаторы только на стыке между компонентами. Ограничение модификаторами доступа (с бледными названиями классов) внутри компонента будет способствовать контролю доступа к пакетам на уровне компиляции, в таком случае это не приведет к использованию различных сторонних инструментов для проверки стиля кода.

Альтернативный подход ограничения доступа между разными пакетами реализуется с помощью определения под-модулей вместо пакетов внутри одного проекта [3]. Таким образом, доступ между под-модулями происходит за счет использования инструментов сборки проекта, таких как maven или gradle, в которых устанавливаются зависимости между под-модулями. Например, имеются под-модули А, Б, С с контроллерами, сервисами и репозиториями, соответственно. В сборщике проекта устанавливается последовательная зависимость А от Б и Б от С, следовательно, А не имеет доступа к С. Предложенный ранее подход с применением модификаторов доступа возлагает ответственность на разработчика, каждый раз, когда добавляются новые классы, что может привести к возможному нарушению доступа. В случае разбиения на под-модули, доступ между ними определяется единожды внутри инструмента по сборке проекта. Следующим преимуществом, является полное разграничение зависимостей, что особо применимо в стратегии «Порты и адаптеры», здесь, все связанные зависимости находятся внутри каждого модуля, который физически представляет собой jar-файл. Такой способ организации увеличивает контроль над возможностью использовать внешние библиотеки внутри каждого модуля, также, упростит и явно разграничит тестирование функционала.

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

Заключение

Рассмотрены возможные подходы в организации кода, которые помогут облегчить восприятие бизнес-модели и установить обязанности программных компонентов. Несмотря на преобладание разработки с использованием подхода «Упаковки по уровням», необходимо распространять другие подходы, которые имеют ряд преимуществ. Очевидно, что большинство разработчиков прибегают к подходу, отталкиваясь от предыдущего опыта, и стараются завершить поставленную задачу в кратчайшие сроки. Например, пренебрежение к чистой архитектуре может спровоцировать осложнения в понимание бизнес-логики, ее изменения и тестирования. Следует освещать основные стратегии и их возможные модификации, а также доносить их актуальность в применении при разработке приложений.

Текст статьи
  1. Роберт С. М. Чистая архитектура. Искусство Разработки программного обеспечения. 2018. С. 288-304.
  2. Vertical Slice Architecture [Электронный ресурс] URL: https://www.jimmybogard.com/vertical-slice-architecture
  3. Package by Component with Clean Modules in Java [Электронный ресурс] URL: https://blog.ttulka.com/package-by-component-with-clean-modules-in-java
  4. Presentation Domain Data Layering [Электронный ресурс] URL: https://martinfowler.com/bliki/PresentationDomainDataLayering.html
Список литературы