Книга Чистая архитектура. Искусство разработки программного обеспечения, страница 68. Автор книги Роберт Сесил Мартин

Разделитель для чтения книг в онлайн библиотеке

Онлайн книга «Чистая архитектура. Искусство разработки программного обеспечения»

Cтраница 68

Чистая архитектура. Искусство разработки программного обеспечения

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


Обратите внимание на стрелки между типами на рис. 34.7: они не зависят от архитектурного стиля, который вы пытаетесь воплотить. Концептуально стили очень разные, но синтаксически они идентичны. Кроме того, можно даже утверждать, что после объявления всех типов общедоступными эти четыре стиля превращаются лишь в четыре способа описания традиционной архитектуры с горизонтальными уровнями. Это ловкий фокус, и, конечно же, никто не будет объявлять все свои Java-типы общедоступными. За исключением случаев, когда это действительно делается. И я видел их.

Модификаторы доступа в Java не идеальны [71], но их игнорирование может вызывать проблемы. Порядок распределения типов Java по пакетам фактически может иметь большое значение для доступности (или недоступности) этих типов, когда модификаторы доступа применяются соответственно. Если вернуть пакеты и отметить те типы (сделав их бледнее на диаграмме), которым можно дать более ограничивающий модификатор, картина станет интереснее (рис. 34.8).

Двигаясь слева направо в подходе «упаковка по уровням», интерфейсы OrdersService и OrdersRepository должны быть объявлены общедоступными, потому что имеют входящие зависимости от классов, находящихся за пределами пакета, в котором объявлены эти интерфейсы. Классам с реализациями (OrdersServiceImpl и JdbcOrdersRepository), напротив, можно придать более ограниченную видимость (на уровне пакета). Никто не должен знать об их существовании; они являются деталями реализации.

В подходе «упаковка по особенностям» единственной точкой входа в пакет является OrdersController, поэтому доступ ко всему остальному можно ограничить рамками пакета. Важно отметить, что в такой ситуации никакой другой код, находящийся за пределами этого пакета, не сможет получить никакой информации о заказах в обход контроллера. Иногда это может быть нежелательно.

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


Чистая архитектура. Искусство разработки программного обеспечения

Рис. 34.8. Бледно-серым цветом выделены типы с более ограничивающим модификатором доступа


можно ограничить рамками пакета и внедрять зависимости во время выполнения.

Наконец, в подходе «упаковка по компонентам» интерфейс OrdersComponent имеет входящую зависимость от контроллера, но доступ ко всему остальному можно ограничить рамками пакета. Чем меньше общедоступных типов, тем меньше число потенциальных зависимостей. В данном случае код за пределами этого пакета не имеет возможности [72] напрямую использовать интерфейс OrdersRepository или его реализацию, поэтому соблюдение архитектурного принципа можно переложить на компилятор. То же самое можно проделать в .NET с помощью ключевого слова internal, но при этом придется создать отдельные сборки для всех компонентов.

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

Другие режимы разделения

Кроме средств, поддерживаемых языком программирования, часто существуют другие способы разделения зависимостей в исходном коде. Для Java имеются свои инфраструктуры, такие как OSGi, и новейшая система модулей в Java 9. При правильном использовании системы модулей позволяют разделить типы, объявленные как public, и публикуемые (published) типы. Например, можно создать модуль Orders, в котором все типы объявлены как public, но опубликовать только ограниченное число этих типов для внешних потребителей. Это долгожданное нововведение, и я с энтузиазмом воспринимаю появление новой системы модулей Java 9, которая даст нам еще один инструмент для создания хорошего программного обеспечения и вновь разбудит в людях интерес к дизайнерскому мышлению.

Другая возможность разделить зависимости на уровне исходного кода — создать несколько разных деревьев с исходным кодом. Для портов и адаптеров, например, можно было бы создать три таких дерева:

• Исходный код с предметной и бизнес-логикой (то есть все, что не зависит от выбора технологии и фреймворков): OrdersService, OrdersServiceImpl и Orders.

• Исходный код веб-интерфейса: OrdersController.

• Исходный код, реализующий хранение данных: JdbcOrdersRepository.

Последние два дерева имеют зависимости времени компиляции от кода с предметной и бизнес-логикой, который сам по себе ничего не знает о веб-интерфейсе и особенностях хранения данных. С точки зрения реализации это можно сделать, настроив отдельные модули или проекты в конфигурации инструмента сборки (например, Maven, Gradle, MSBuild). В идеале этот шаблон можно применить для отделения деревьев с исходным кодом всех компонентов приложения.

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

Более простой подход, используемый теми, кто реализует архитектуру портов и адаптеров, заключается в создании двух деревьев с исходным кодом:

• Предметный код («внутренняя» область).

• Инфраструктурный код («внешняя» область).

Его хорошо иллюстрирует диаграмма (рис. 34.9), которую многие используют для обобщения архитектуры портов и адаптеров с ее зависимостью времени компиляции инфраструктурного кода от предметного.


Чистая архитектура. Искусство разработки программного обеспечения

Рис. 34.9. Предметный и инфраструктурный код


Такой подход к организации исходного кода тоже дает положительные результаты, но вы должны помнить о потенциальных компромиссах. Это то,  что я называю «окружным антишаблоном портов и адаптеров». В Париже (Франция) имеется окружная автодорога с названием Boulevard Périphérique (бульвар Периферик), позволяющая обогнуть Париж, не утруждая себя сложностями движения внутри города. Включение всего инфраструктурного кода в единое дерево подразумевает потенциальную возможность для кода из любой части приложения (например, веб-контроллера) напрямую вызывать код из другой части приложения (например, функции для работы с базой данных), без пересечения предметной области. Это особенно верно, если забыть применить в том коде соответствующие модификаторы доступа.

Вход
Поиск по сайту
Ищем:
Календарь
Навигация