Перейти к содержимому

11. Стандартная библиотека

До сих пор мы использовали только базовый язык: module, surface, interface. Три базовых типа. Каждый пример был помеченной рамкой с операциями и стрелками.

Этого достаточно, чтобы смоделировать что угодно структурно, но не хватает двух вещей, которые нужны реальным проектам:

  • Визуальное различие. База данных, сервис, пользователь и внешний поставщик должны выглядеть на диаграмме по-разному. Только с module они так не выглядят.
  • Принудительные соглашения. Каждый сервис в этой организации имеет команду-владельца. Базовый тип module ничего не принуждает; валидатор не отличит сервис от внешнего клиента.

Стандартная библиотека решает обе задачи. Она поставляет пакеты типов — arch.extras, arch.c4, arch.backend, arch.diagrams, — которые вводят семантические подтипы базовых типов. Каждый каскадирует виджет по умолчанию и (как правило) требует несколько полей.

Правило. Стандартная библиотека — это необязательные батарейки, а не обязаловка. Это хороший, исчерпывающий набор по умолчанию, прикрученный к базовому языку: опирайтесь на него, чтобы стартовать, но вы вольны построить собственную библиотеку с нуля, и начало работы на типах стандартной библиотеки вас ни к чему не привязывает. Позже вы можете заменить тип из стандартной библиотеки своим или расширить его (добавить требования, поля, варианты связывания), а не переписывать.

Эта глава знакомит с тем, что есть в стандартной библиотеке и как подключить её к проекту.

Объявите нужные типы в package.archspace, чтобы палитра была общей для пространства — каждый файл .arch в пакете может до неё дотянуться, а манифест заодно служит обозримым списком того, что в ходу:

package: my-project
// Относитесь к каждому пакету как к палитре — берите типы, которые реально используете.
use service, database, message_broker, gateway from arch.backend
use rest_create, rest_read, rest_update, kafka, grpc_unary from arch.backend
use user, usergroup from arch.extras
use table, column from arch.diagrams

Правило. Никогда не делайте use * для стандартной библиотеки. Относитесь к пакету стандартной библиотеки как к палитре, из которой выбирают, и импортируйте по имени ту горстку типов, которая вам нужна. Wildcard затаскивает всю поверхность пакета и привязывает вас ко всему ней. (use * оправдан только для небольшой внутренней библиотеки, которую вы написали сами, — никогда для стандартной библиотеки.) Держите эти строки use в манифесте package.archspace, а не переимпортируйте файл за файлом: один список на уровне пространства самодокументируем.

arch.backend — это прагматичный словарь архитектуры: ключевые backend-типы модулей (service, database, gateway, message_broker, …), общие типы system / frontend / component / external_system и типы интерфейсов на уровне протокола (http_*, grpc_*, kafka, amqp, db_read, db_write, …). arch.extras поставляет общие фигуры устройств и людей (user, usergroup, laptop, server, cloud, …). arch.diagrams добавляет table / column для ER-схем. Все пакеты автоматически разрешаются через встроенную в инструментарий стандартную библиотеку — запись в dependencies не нужна.

Предпочитаете формальную модель C4? arch.c4 — это полный, самодостаточный словарь C4: person, software_system, container + подтипы по форм-фактору, component, внешние/граничные варианты и узлы развёртывания. Импортируйте те типы C4, которыми вы декомпозируете, — use person, software_system, container, component from arch.c4 — и оставайтесь в фиксированном наборе абстракций C4. Такая библиотека — больше, чем удобство: C4 — это метамодель, нотация и дисциплина, которая формирует то, как вы декомпозируете и рассуждаете. Вы принимаете её ради того, что она делает с вашим мышлением, а не только ради рамок, которые она вам выдаёт. (В отличие от arch.extras, чьи фигуры user / group — чисто приятные дополнения, делающие диаграмму нагляднее.)

Теперь ваши файлы .arch могут использовать более выразительные типы:

service Payments {
aspect team: "Payments"
aspect { domain: "Payments" }
rest_create authorize
kafka paymentEvents
}

Импортируемые типы — это подтипы module и interface. Все правила из глав 4 и 5 применимы — поверх просто добавляются значения по умолчанию и требования.

Наиболее распространённые backend-типы модулей:

ТипПакетОбязательные поляЗначения по умолчаниюВиджет по умолчанию
servicearch.backendaspect team, aspect domainarch-module
databasearch.backendaspect team, aspect data.classificationarch-backend-database
message_brokerarch.backendaspect team, aspect domainarch-module

Столбцы:

  • Обязательные поля должны быть заполнены или сброшены в каждом экземпляре. Их отсутствие — ошибка разбора.
  • Значения по умолчанию — предустановленные поля, которые наследует экземпляр; каскад распространяет их на вложенные модули.
  • Виджет по умолчанию — тег пользовательского элемента, который монтирует средство рендеринга. У каждого типа своя визуальная идентичность.

Когда что использовать:

  • service — серверный сервис с поддерживающей командой.
  • database — хранилище данных. Подтипы по семейству (relational, document, kv_store, …) и по движку (postgres, mongodb, redis, …).
  • message_broker — развёрнутый брокер (кластер Kafka, RabbitMQ, NATS, …).

Используйте базовый тип module, когда ни один из этих не подходит — обычно редко. Если хочется «почти service, но ещё с тремя обязательными полями», определите подтип в рамках проекта (Глава 16).

arch.c4 — это полный словарь модели C4: четыре уровня абстракции (Context, Containers, Components, Code) за вычетом Code, которым является сам исходный код. Цвета следуют канонической палитре C4, поэтому диаграммы читаются так же, как в официальной нотации.

ТипУровеньОбязательные поляЗначения по умолчаниюВиджет по умолчанию
personContextaspect team, aspect domainarch-c4-person
external_personContextarch-c4-external-person
software_systemContextaspect team, большая карточкаarch-module
external_software_systemContextrequired ext.vendor, required ext.contract.urlarch-module (пунктир, чип vendor)
containerContaineraspect team, aspect technologyarch-module
componentComponentaspect teamarch-module
enterprise_boundaryBoundaryбольшая пунктирная карточкаarch-c4-boundary
system_boundaryBoundaryбольшая пунктирная карточкаarch-c4-boundary
container_boundaryBoundaryбольшая пунктирная карточкаarch-c4-boundary

container через подтипы предустанавливает глиф заголовка для распространённых форм-факторов: web_application, single_page_app, mobile_app, api, data_store, message_bus, file_system, serverless_function. Каждый остаётся container для любого правила или шага процесса, фильтрующего по родительскому типу.

Когда что использовать:

  • person — человек-пользователь, роль или персона. external_person находится за пределами рассматриваемого предприятия (клиент, партнёр) и рендерится приглушённым + пунктирным.
  • software_system — высший уровень абстракции; приносит ценность своим пользователям. external_software_system — система, которая вам не принадлежит; ext.vendor / ext.contract.url обязательны, чтобы внешние зависимости всегда документировали, что они такое и где находится их контракт.
  • container — отдельно разворачиваемая/запускаемая единица (веб-приложение, API, база данных, очередь). НЕ Docker-контейнер, хотя может им быть. aspect technology фиксирует технологический стек так, как C4 печатает его под именем.
  • component — группировка функциональности внутри контейнера за чётко определённым интерфейсом. Отдельно не разворачивается.
  • границы — пунктирные группирующие прямоугольники, размещающие подпространство дочерних элементов: enterprise_boundary (люди + системы одной организации), system_boundary (контейнеры одной системы), container_boundary (компоненты одного контейнера).

Отношения («uses», «sends data to», …) не являются типами модулей — в ArchLang они вытекают из шагов process и соединений interface между этими узлами, как и в любом другом пакете.

Дополнительная диаграмма развёртывания C4 отображает логические контейнеры на реальную инфраструктуру. arch.c4 поставляет и её, отрисовывая серо-сланцевым цветом, чтобы отделить физический слой от синих логических элементов:

ТипОбязательные поляЗначения по умолчаниюВиджет по умолчанию
deployment_nodeaspect team, aspect technology, большая карточкаarch-module
infrastructure_nodeaspect team, aspect technologyarch-module
container_instance(подтип container) чип aspect instancesarch-module
  • deployment_nodeгде выполняется программное обеспечение. Свободно вкладывается (cloud → region → cluster → host) и размещает подпространство. Разновидности предустанавливают глиф: cloud_platform, region, cluster, host, execution_environment, device.
  • infrastructure_node — вспомогательное сетевое оборудование, не являющееся контейнером: load_balancer, firewall, dns, cdn, gateway.
  • container_instance — развёрнутая копия container; задайте aspect instances для количества реплик («x3»). aspect technology — это строка, которую C4 печатает под именем каждого узла.

arch.c4 самодостаточен — он покрывает всю модель C4 (Context, Container, Component, Boundaries, Deployment) самостоятельно. Используйте его, когда хотите оставаться в фиксированном словаре C4, а не в открытой вложенности и аспектах ArchLang.

Типы интерфейсов на уровне протокола, сгруппированные по транспорту:

ГруппаТипы
HTTPhttp_get, http_post, http_put, http_patch, http_delete, http_head, http_options, webhook, sse
RESTrest_list, rest_create, rest_read, rest_update, rest_delete (+ поверхность rest_crud)
gRPCgrpc_unary, grpc_server_stream, grpc_client_stream, grpc_bidi_stream
GraphQLgraphql_query, graphql_mutation, graphql_subscription
WebSocketwebsocket
Messagingkafka, amqp, nats, mqtt, redis_pubsub
Доступ к даннымdb_read, db_write

Соглашение по оформлению рёбер: синхронные request/response — сплошная линия; асинхронные / стримовые / pub-sub — пунктирная. Стримовые варианты (grpc_server_stream, webhook, sse, graphql_subscription) переопределяют свой сплошной родитель на пунктирный.

Асинхронные/событийные интерфейсы (любые пунктирные: kafka, amqp, nats, mqtt, redis_pubsub, webhook, sse, graphql_subscription, grpc_*_stream) — это то, чем вы моделируете события. Событие — это просто один из таких асинхронных интерфейсов, достигаемый обычным ребром процесса >; нет ни поля subscribes:, ни отдельной конструкции события. Именно пунктирный тип помечает вызов как асинхронный; ребро и есть подписка.

Базовый язык против стандартной библиотеки: одна и та же модель

Заголовок раздела «Базовый язык против стандартной библиотеки: одна и та же модель»

Одна и та же архитектура, записанная двумя способами:

Базовая запись:

module Payments {
aspect team: "Payments"
interface authorize
interface orderEvents
}
module Shipping {
aspect team: "Fulfillment"
interface createShipment
}
process Fulfilment {
Shipping > Payments.orderEvents // ребро подписывает Shipping на событие
}

С использованием стандартной библиотеки:

service Payments {
aspect team: "Payments"
aspect { domain: "Payments" }
rest_create authorize
kafka orderEvents
}
service Shipping {
aspect team: "Fulfillment"
aspect { domain: "Fulfillment" }
rest_create createShipment
}
process Fulfilment {
Shipping > Payments.orderEvents // асинхронное ребро — тип kafka рисует его пунктиром
}

Структура идентична — два модуля, три интерфейса, одно ребро процесса, подписывающее Shipping на событие Payments. Версия со стандартной библиотекой добавляет:

  • Виджеты service (конкретная визуальная идентичность).
  • Типы интерфейсов rest_create / kafka (сплошные sync против пунктирных async).
  • Аспект domain (теперь обязательный, потому что его требует тип).

Базовая версия валидна; версия со стандартной библиотекой — то, что реально поставляют в проектах. Перевод механический.

Когда НЕ стоит использовать стандартную библиотеку

Заголовок раздела «Когда НЕ стоит использовать стандартную библиотеку»

Несколько сценариев, где базовые типы — или ваша собственная библиотека — имеют смысл:

  • Изучение языка. В главах 2–10 использовались базовые типы именно поэтому — меньше движущихся частей, которые нужно держать в голове.
  • Крошечные черновые проекты без манифеста. Анонимные пакеты не могут ничего импортировать через use, поэтому базовые типы — всё, что доступно. (Однофайловые черновики — полноправны, см. Глава 12.)
  • Создание собственной библиотеки. Библиотеки дёшево писать. Крупной организации часто нужна собственная — обычно не ради виджетов (виджеты стандартной библиотеки переиспользуемы), а ради специфичной для организации семантики: требований к владению/сопровождению, отношений между подразделениями, фирменной нотации. Постройте её с нуля или расширьте типы стандартной библиотеки, а не заменяйте их.
  • Создание собственного инструментария. Если вы встраиваете ArchLang в систему, которая уже определяет собственную онтологию, может быть желательно полностью обойти словарь стандартной библиотеки.

Стандартная библиотека — удобное значение по умолчанию, а не требование: большинство проектов опираются на неё, заменяют части по мере перерастания и расширяют там, где этого требуют внутренние правила.

Глава 12: Пакеты → — подробно о манифесте: зависимости, версия, экспорт типов.