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

10. Описания

Описание — это голая строка в любом теле: модуля, поверхности, интерфейса, проекции, процесса. Рендереры обращаются с текстом описания как с markdown с двумя расширениями: перекрёстные ссылки ([[…]]) и интерполяция значений (@field / @@aspect). Они появляются везде, где показываются описания: всплывающие подсказки, документация автодополнения, боковые панели, тела виджетов — и отдельные файлы .md используют ровно тот же диалект (Глава 10a).

module Payments {
aspect team: "Payments"
aspect {
domain: "Payments"
security.zone: "PCI"
}
"
# Payments
Core payment processor for the **@@domain** domain, operating
in the *@@security.zone* zone. Published events are consumed
by [[Orders]] and [[Notifications]].
> Every transaction in the platform flows through this service.
"
interface authorize
interface refund
}

Описание рендерится с markdown-форматированием, с подставленными @@domain и @@security.zone, и с [[Orders]] и [[Notifications]], отрендеренными как кликабельные ссылки на соответствующие модули.

У описания две законные формы; выбирайте ту, что подходит элементу.

Режим 1 — короткая однострочка. Скажите то, что не говорит одно лишь имя, и остановитесь.

Режим 2 — более длинный документ. Полный Markdown: внутреннее устройство, обзор API, заметки по дизайну — настоящий документ, если элемент того заслуживает.

Что не законно — так это пересказывать очевидное. Не повторяйте то, что уже говорят имя, вложенные модули, интерфейсы или процессы.

Правило. Избыточное описание хуже, чем никакого. Для PaymentProcessor «Обрабатывает платежи» хуже, чем пустое описание, — оно добавляет длину, не добавляя информации. Полное отсутствие описания — это совершенно нормально, когда добавить нечего неочевидного.

Структурированные ссылки место в полях, а не в прозе

Заголовок раздела «Структурированные ссылки место в полях, а не в прозе»

Описание — для прозы. Ссылка на внешнюю спецификацию или документ — страница Confluence, документ OpenAPI, запись в каталоге данных — это свойство элемента, а свойства идут в поля (Глава 9):

module Orders {
repo.url: "https://github.com/acme/orders"
docs.url: "https://wiki.acme.com/orders"
api.spec: "https://specs.acme.com/orders/openapi.yaml"
"Order intake, validation, and fulfilment hand-off."
}

Помещение их в поля сохраняет их структурированными и пригодными для запросов — инструменты могут отрисовать их как кнопки, а модель становится хабом ссылок. Более длинное Markdown-описание всё ещё может нести случайную встроенную ссылку в своей прозе, но канонический указатель «где живёт спецификация» — это поле.

Поддерживаются следующие конструкции CommonMark:

КонструкцияПример
Жирный**bold**
Курсив*italic*
Зачёркивание~~struck~~
Встроенный код`code`
Заголовки# H1, ## H2, ### H3
Спискиупорядоченные, неупорядоченные, вложенные
Таблицытаблицы на вертикальных чертах в стиле GFM
Цитаты> note
Блоки кодаогороженные тройными обратными кавычками, опционально с языком
Горизонтальная линия---
Внешние ссылки[text](https://...)

Следующее не поддерживается и либо рендерится как литеральный текст, либо вырезается:

  • Сырой HTML (граница безопасности — рендереры агрессивно очищают).
  • Изображения (описания — это текст; визуальное место в виджетах и проекциях).
  • Сноски, списки определений, списки задач.
  • Автоматическое связывание голых URL (используйте явное [text](url)).

Заголовки (#, ##, ###) все рендерятся с одинаковым визуальным рангом. У описаний один уровень заголовков; глубина не несёт семантического веса. Вы вольны использовать несколько # для читаемости исходника, но не полагайтесь на визуальную иерархию между H1 и H3.

Причина: описания появляются во всплывающих подсказках, боковых панелях и других стеснённых контекстах, где воспроизведение полной иерархии документа выглядит неуместно. Воспринимайте заголовки как названия разделов, а не как вложенность.

[[…]] ссылается на другое объявление в модели. Две формы:

По стабильному идентификатору:

"Routes to [[#r3n8wt]] for downstream processing."

Всегда разрешается, если цель существует в рабочем пространстве. Рендерится как человекочитаемое имя цели, связанное с местом её объявления.

По человекочитаемому имени:

"Published events are consumed by [[Orders]] and [[Notifications]]."

Разрешается по всему рабочему пространству. Если два объявления имеют общее имя, ссылка неоднозначна — рендерер помечает её как ошибку, а LSP выдаёт диагностику с перечислением кандидатов. Снимите неоднозначность, переключившись на форму с идентификатором.

Имена могут быть с пространством имён; форма с пространством имён сопоставляется целиком:

"See [[Personal.Banking.Payments]] for the legacy path."

Ссылки, которые не разрешаются, рендерятся как маркеры ошибки и выдают предупреждения LSP — опечатки всплывают сразу, а не гниют молча.

Внутри скобок ведущий @@ помечает ключ аспектаплоскость (Глава 9) — а не имя элемента:

"Runs in [[@@security-zone:pci]]; part of the [[@@domain]] plane."
  • [[@@key]] ссылается на всю плоскость, названную ключом аспекта (. разделяет сегменты пути ключа).
  • [[@@key:value]] ссылается на конкретное место на ней — аспект, — где : всегда вводит значение.

Они разрешаются в набор элементов, несущих этот аспект, и ведут на оверлей аспекта, а не на одно объявление.

Ссылка [[Doc#section]] указывает на заголовок # внутри документа .md (Глава 10a); голая [[Doc]] ссылается на документ целиком.

[[ref]]@@aspectPath — это перекрёстная ссылка, скомпонованная с геттером значения (ниже): она читает аспект на том узле, на который ссылается, а не на владеющем.

"Part of the [[#pay001]]@@domain domain." // читает аспект domain у Payments

Скобочной формы [[Name@field]] нет — доступ к чужому значению всегда идёт через скомпонованное [[ref]]@@aspectPath, и поскольку оно несёт собственный контекст, работает везде, включая отдельные файлы .md.

Каждый элемент ведёт автоматический индекс «упоминается в» из всех ссылок [[…]], указывающих на него, — и в описаниях, и в документах .md. Инструменты показывают его на элементе, так что вы можете перепрыгнуть от объявления ко всем местам, где о нём говорится.

Геттер подставляет значение в описание того же узла, к которому он прикреплён. Сигил называет ось: @path читает поле, @@path читает аспект — зеркаля поля key: value и блоки aspect { }, из которых берутся пути (Глава 9).

module Payments {
version: "1.2"
aspect {
domain: "Payments"
sla.tier: "gold"
}
"Version @version. SLA tier: @@sla.tier. Operates in the @@domain domain."
}

Рендерится примерно как: «Version 1.2. SLA tier: gold. Operates in the Payments domain.»version — поле, читается одним @; sla.tier и domain — аспекты, читаются @@.

Разрешение идёт по цепочке типов — если узел не объявляет поле или аспект сам, но наследует его от родительского типа, используется унаследованное значение.

@@ читает аспекты на владеющем узле, никогда — на узлах, на которые есть ссылки (то же самое верно для чтения голого поля @):

module A {
aspect { domain: "Sales" }
"Domain: @@domain" // resolves to "Sales"
}
module B {
aspect { domain: "Ops" }
"Other module's domain: @@domain" // resolves to "Ops" (B's own aspect),
// NOT A's domain
}

Голый @@ на модуле A не может читать аспекты на модуле B — для этого используйте чужой геттер [[B]]@@domain (выше). А поскольку голому геттеру нужен владеющий узел, он только для описаний: в отдельном документе .md (у которого нет владеющего узла) голый @ или @@ — ошибка, поэтому документы используют исключительно [[ref]]@@aspectPath (Глава 10a).

Если @@aspectPath не разрешается, рендерер выводит литеральную метку-страж <missing:aspectPath>, а LSP выдаёт предупреждение в месте интерполяции. Тихий откат к пустой строке запрещён — опечатки и устаревшие ссылки обязаны быть видимы. (То же правило действует и для отсутствующего @field.)

module C {
"Owner: @@team" // no 'team' aspect declared or inherited
}

Рендерится как: «Owner: <missing:team>» с предупреждением LSP.

В теле может быть несколько голых строк. Они соединяются через \n в порядке объявления:

module Payments {
"First paragraph about the service."
aspect team: "Payments"
"Second paragraph, declared after the team aspect. Order in the source
doesn't matter for resolution but does matter for description joining."
interface authorize
}

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

Реальное описание из демо Payments:

module #k7m2qx Payments {
aspect team: "Payments"
docs.runbook: "https://wiki.acme.com/pci"
aspect {
domain: "Payments"
security.zone: "PCI"
criticality: "High"
}
"
Core **payment processing** service for the *@@domain* domain.
Operates in the `@@security.zone` zone with criticality *@@criticality*.
> Every transaction in the platform flows through this service
> before reaching an external processor.
**Capabilities.** Authorize, capture, and refund transactions;
route to processors via [[#r3n8wt]]; publish `paymentEvents`
consumed by [[Orders]] and [[Notifications]].
**Compliance.** PCI-DSS scope.
"
interface authorize
interface refund
interface paymentEvents
}

Это рендерится как полная markdown-карточка с подстановками аспектов и живыми перекрёстными ссылками на #r3n8wt, Orders и Notifications. Обратите внимание: URL рунбука — это поле (docs.runbook), а не ссылка, зарытая в прозе, — структурированные ссылки остаются структурированными.

Резолвер перекрёстных ссылок и интерполятор аспектов — часть пакета core, а не только LSP. И LSP (для диагностик и всплывающих подсказок), и клиентские просмотрщики (для рендеринга) используют один и тот же резолвер. Итог: описания выглядят идентично везде, где они показываются.

  • Описания — голые строки, обрабатываемые как markdown с двумя расширениями ArchLang.
  • [[#id]] и [[Name]] ссылаются на объявления; [[@@key:value]] ссылается на аспект, [[Doc#section]] — на заголовок документа.
  • @field подставляет поле, @@aspect подставляет аспект — оба на том же узле (только для описаний); [[ref]]@field / [[ref]]@@aspect читает значение с другого узла и работает везде.
  • Каждый элемент получает автоматические обратные ссылки («упоминается в»), охватывающие описания и файлы .md.
  • Заголовки все рендерятся с одним рангом; сырой HTML и изображения не поддерживаются.
  • Отсутствующие ссылки и отсутствующие пути аспектов всплывают как ошибки, никогда не тихо.
  • Несколько описаний голыми строками склеиваются.
  • Тот же диалект рендерит отдельные документы .md — первоклассную поверхность архпространства (Глава 10a).

Глава 10a: Markdown-документы → — файлы .md как первоклассная поверхность архпространства, использующая ровно тот же флавор-markdown диалект.