4. Модули
Модуль — это архитектурная единица с одним ответственным сопровождающим, чёткой границей и публичной поверхностью, состоящей из интерфейсов. Это та примитивная сущность, к которой крепится всё остальное.
module Payments { team: Payments "Authorizes and captures card payments."
interface Authorize interface Capture interface Refund}Это модуль. Вид — module; имя — Payments; поле team указывает, кто им владеет; описание и три интерфейса раскрывают, что он делает.
Эта и несколько следующих глав используют только базовые виды языка — module, facet, interface. Стандартная библиотека добавляет более богатые виды вроде service, database, command, event (см. Главу 11). Всё, что есть в этой главе, применимо и к ним; они являются подтипами module.
Правило
Правило. Если у этого есть владелец и граница — это модуль.
Это весь тест. Сервисы — модули. Базы данных — модули. Внешние системы — модули. Люди и внешние клиенты (актёры) — модули. Подсистемы, содержащие другие модули, — модули. «Библиотека», лежащая внутри репозитория одной команды, со своей контрактной поверхностью, — модуль.
Если вы ловите себя на вопросе «это модуль или что-то другое?» — и у этого есть владелец и граница — это модуль.
Базовый вид module
Базовый вид — module. У него нет обязательных полей, нет интерфейсов по умолчанию и нет специального виджета. Это самое общее возможное объявление модуля:
module Orders { interface CreateOrder interface CancelOrder}Это полноценный модуль. Два интерфейса, ни команды, ни описания, ни меток. Валидатор его принимает. Рендерер диаграммы рисует его как подписанный прямоугольник.
Большинство архитектур не используют базовый module напрямую. Они используют вид из стандартной библиотеки, такой как service, или вид, определённый в проекте, такой как payment_service — оба являются подтипами module, которые добавляют требования к команде, виджеты и соглашения. Но базовая форма всегда доступна, и именно к ней сводится каждый более богатый вид.
Вложенность
Модули могут содержать другие модули. Используйте любую из форм:
Прямая вложенность:
module Platform { team: "Platform Engineering"
module AuthService { interface Authenticate }
module UserService { interface GetUser }}Плоская через in:
module Platform { team: "Platform Engineering"}
module AuthService in Platform { interface Authenticate}
module UserService in Platform { interface GetUser}Обе формы дают идентичную структуру. Форма с in позволяет держать объявление родителя в одном файле и позволять командам добавлять дочерние модули из своих собственных файлов, не редактируя одно и то же место.
Viewer отрисовывает вложенные модули как контейнеры. module Platform становится рамкой; AuthService и UserService становятся узлами внутри неё.
Поля
Тело модуля — это в основном поля. Поле имеет вид key: value:
module Payments { team: Payments repo.url: "https://github.com/acme/payments" version: v2 ext.cmdb.ci: "CI28304858" "Core payments service"}Ключи полей могут быть точечными (repo.url, ext.cmdb.ci). Значения — идентификаторы (Payments, v2), строки в кавычках, числа или булевы. Это весь язык значений — никаких вложенных объектов, никаких типов на поле. Глава 9 подробно покрывает поля.
Голая строка "Core payments service" — это особое поле: описание. В теле модуля может быть несколько описаний; они склеиваются. Глава 10 посвящена им.
Интерфейсы
Интерфейсы — это то, как модули показывают себя другим модулям. Внутри тела модуля объявления интерфейсов соседствуют с полями:
module Orders { team: Commerce
interface CreateOrder interface CancelOrder interface GetOrder interface OrderEvents}Ключевое слово interface — это базовый вид интерфейса. Стандартная библиотека определяет подтипы вроде command, query и event, которые несут семантическую информацию (синхронный или асинхронный, чтение или запись). Пока что каждый интерфейс — это просто interface. Глава 5 подробно разбирает интерфейсы; Глава 11 знакомит с видами интерфейсов из стандартной библиотеки.
Интерфейсы всегда листовые: интерфейс не содержит другой интерфейс. Чтобы их сгруппировать, используйте фасеты — Глава 6.
Стабильные идентификаторы
После того как вы сохраните файл .arch через форматтер, каждый модуль получает стабильный идентификатор:
module #pay001 Payments { team: Payments}Префикс #pay001 закрепляет идентичность модуля при переименованиях. Переименуйте Payments в PaymentsService — идентификатор останется тем же, и инструменты вместе с диффами понимают, что это тот же модуль. Глава 13 подробно возвращается к этому. Пока что: не пишите идентификаторы вручную; пусть их выпускает форматтер.
Пустые тела
Если модулю нечего добавить, тело можно полностью опустить:
module NotificationsЭто валидно. Модуль существует без полей и без интерфейсов. Полезно как заглушка или когда сама идентичность модуля и есть весь архитектурный факт.
Резюме
- Модуль — это всё, у чего есть владелец и граница.
- Базовый вид —
module. Каждый более богатый вид (service, database, actor, …) является его подтипом. - Тела содержат поля, описания, интерфейсы, фасеты и вложенные модули — в любом порядке.
- Модули могут вкладываться напрямую или прикрепляться к объявленному родителю через
in Parent. - Стабильные идентификаторы (
#xyz) закрепляют идентичность при переименованиях; форматтер выпускает их при сохранении.
Что дальше
Глава 5: Интерфейсы → — как модули показывают себя друг другу.