Также отметьте, что отношения
Заключение
Диаграмма архитектуры на рис. 33.2 включает разделение по двум измерениям. Первое — разделение на основе действующих лиц согласно принципу единственной ответственности; второе соответствует правилу зависимости. Цель обоих — разделить компоненты, изменяющиеся по разным причинам и с разной скоростью. Причины в данном случае соответствуют действующим лицам, а скорости — разным уровням политик.
После структуризации кода таким способом появляется возможность смешивать и распределять его по единицам развертывания как угодно. Вы сможете группировать компоненты в любые единицы развертывания, имеющие смысл, и менять правила группировки с изменением условий.
Глава 34. Недостающая глава
Все советы, которые вы прочитали к настоящему моменту, безусловно, помогут вам проектировать замечательные приложения, состоящие из классов и компонентов с четко определенными границами, понятными обязанностями и управляемыми зависимостями. Но как всегда, дьявол кроется в деталях реализации, и действительно, очень легко споткнуться о последнее препятствие, если не уделить ему должного внимания.
Представим, что мы строим книжный онлайн-магазин и один из вариантов использования, который нам предлагается внедрить, — возможность просмотра клиентами состояния своих заказов. Пример, следующий ниже, описывается с позиции языка Java, однако принципы в равной степени применимы к другим языкам программирования. Отложим пока чистую архитектуру в сторону и рассмотрим несколько подходов к проектированию и организации кода.
Упаковка по уровням
Первый и самый простой, пожалуй, подход — организация традиционной многоуровневой архитектуры, в которой код разделяется по функциональному признаку. Этот подход часто называют «упаковкой по уровням». На рис. 34.1 показано, как могла бы выглядеть соответствующая UML-диаграмма классов.
Рис. 34.1. Упаковка по уровням
В такой типичной многоуровневой архитектуре один уровень выделяется для веб-кода, один уровень — для «бизнес-логики» и один уровень — для работы с хранилищем данных. Иными словами, горизонтальные уровни используются как способ группировки по подобию. В «строгой многоуровневой архитектуре» уровни должны зависеть только от следующего смежного уровня. В Java уровни обычно реализуются в виде пакетов. Как показано на рис. 34.1, все зависимости между уровнями (пакетами) направлены вниз. В этом примере у нас имеются следующие Java-типы:
• OrdersController: веб-контроллер, иногда что-то вроде MVC-контроллера из Spring, обрабатывающего запросы из Веб.
• OrdersService: интерфейс, определяющий «бизнес-логику», связанную с заказами.
• OrdersServiceImpl: реализация службы заказов[63].
• OrdersRepository: интерфейс, определяющий порядок доступа к информации о заказах в хранилище.
• JdbcOrdersRepository: реализация интерфейса хранилища.
В своей статье
Другая проблема, как уже сказал «дядюшка Боб»[65], — многоуровневая архитектура не кричит о своем практическом назначении. Поместите рядом код двух многоуровневых архитектур из разных предметных областей, и они почти наверняка будут выглядеть пугающе похожими: веб-интерфейсы, службы и хранилища. Многоуровневые архитектуры страдают еще от одного большого недостатка, но мы поговорим о нем позже.
Упаковка по особенностям
Другой вариант организации кода — «упаковка по особенностям». Это разделение по вертикали, основанное на объединении связанных особенностей, предметных понятий и общих корней (если использовать терминологию предметно-ориентированного проектирования). В типичных реализациях, которые мне доводилось видеть, все типы помещаются в один Java-пакет, имя которого отражает идею группировки.