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

8. Проекции

Модель одна. Аудиторий много. Платформенного инженера волнует топология сервисов; ревьюера по безопасности — какие сервисы касаются PCI-данных; продакт-менеджера — пользовательский поток. Всем им нужны разные диаграммы одной и той же базовой архитектуры.

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

view PaymentsLandscape {
"Payments domain, grouped by owning team"
show @@domain:"Payments"
hide database
group by @@team
}

Читается сверху вниз: показать каждый элемент, несущий аспект domain: "Payments", скрыть базы данных, сгруппировать оставшееся по командам.

Три идеи пронизывают всё, что связано с проекциями:

  1. Доска — это проекция. Проекции — единственный путь рендеринга. Открытие модели без проекции рендерит синтезированную доску по умолчанию; открытие вкладки процесса рендерит синтезированную проекцию flow. Нет второго конвейера, который мог бы разойтись с первым.
  2. Проекции никогда не содержат истину. Проекция выбирает, представляет и перестилизовывает — она никогда не объявляет структуру, рёбра или аспекты. Всё это принадлежит модели. Переопределения стиля применяются только при рендеринге; каждый селектор и геттер читает настоящую модель под ними.
  3. Клаузы упорядочены. Тело проекции — это зона заголовка (описание, knob-параметры), за которой следуют клаузы, читаемые сверху вниз: сначала show, затем hide, затем group, затем style. Ревьюер читает проекцию так же, как она вычисляется.

Без проекций у вас одна диаграмма: каждый модуль, каждый интерфейс, каждая стрелка процесса — всё сразу. Для всего, что больше крошечной системы, такая диаграмма нечитаема.

Проекции позволяют делать срезы:

  • Показать только домен Payments.
  • Сгруппировать архитектуру по командам для оргсхемы системы.
  • Скрыть всё за пределами PCI-зоны для ревью безопасности.
  • Раскрасить каждый сервис по зоне, в которой он живёт, и отметить рёбра, нарушающие политику.

Каждая проекция — это сохранённый запрос. Получатели открывают тот же URL проекции и видят ту же диаграмму.

Каждая клауза выборки принимает селектор — тот же небольшой язык запросов, что используется везде в ArchLang (голое имя, тип вроде service или database, атом аспекта вроде @@domain:"Payments", шаблон ребра вроде A > B, соединяемые через and / or / not / in).

  • show <sel> — объединяет совпадения в проекцию. Повторяйте свободно; каждый show добавляет ещё. Аргумент-ребро (show A > B) втягивает и само ребро, и его концы — ребро не может отрендериться без своих концов.
  • hide <sel> — вычитает, после объединения show. hide database убирает базы данных; скрытие элемента в середине дерева переподчиняет его видимых потомков ближайшему уцелевшему предку.
  • focus <sel> — только акцент. Он никогда не меняет то, что показано; он визуально отмечает совпадения, чтобы взгляд падал на них.
view CheckoutSurface {
show @@domain:"Orders" or @@domain:"Payments"
hide database
focus Gateway
}

Проекция без show показывает всё — доска по умолчанию, записанная явно, — это просто show *.

group by <getter> кластеризует показанные узлы по значению аспекта. Геттер снабжён сигилом, как всякий геттер — @@ именует ось аспекта:

view ByTeam {
show service
group by @@team
}
  • Второй group by вкладывается внутрь первого.
  • Многозначный аспект помещает элемент в каждую из его групп.
  • Элементы без значения собираются в несгруппированный пул.
  • Порядок групп лексикографический, поэтому диаграмма детерминирована.

Клаузы layout нет. Раскладка — забота солвера; вы не выбираете «elk» или «dagre». Когда нужно закрепить узел на месте, перетащите его в просмотрщике (или напишите style X { pin <x>, <y> }); закрепление живёт на проекции, никогда — на модели.

Одна клауза перестилизовывает узлы, рёбра и свойства виджетов. Тип селектора решает, что именно перестилизуется:

view SecurityZones {
show service or database or gateway
style * { color: colorize(@@security.zone) } // раскрасить каждый узел по его зоне
style violating PciIsolation { color: crimson } // нарушения политики становятся красными
style Payments { pin 120, 340 } // это записывает WYSIWYG-перетаскивание
style service and in Payments {
widget { icon: shield; badge: @@security.zone }
rename "Cards & Payments"
}
}
  • Правая часть переопределения — это выражение. Строковый литерал (color: "#f43"), геттер (widget.badge: @@security.zone) или вычисляемое значение — всё работает.
  • Цвет — единственный лист с рендер-типом, поэтому приведения явные. color: @@team даёт диагностику («team — это значение, а не цвет»); оберните его — color: colorize(@@team) — и легенда выводится из приведения: colorize даёт по одному образцу на значение, heat — градиент. Легенда никогда не лжёт: каждый цвет на доске прослеживается до строки легенды.
  • rename и pin — директивы отображения, а не правки модели. rename "Cards & Payments" меняет подпись только в этой проекции; pin фиксирует позицию. Размер, бейдж и иконка — механика виджета — они идут через оверлей полей (widget.badge: …).
  • style violating <Policy> раскрашивает находки политики (атом violating) — управление руководит визуалом.
  • Наборы (bundles)style bundle DangerLook { … } объявляет переиспользуемое тело стиля; use DangerLook внутри любого style применяет его.

Каждое переопределение применяется только при рендеринге. group by, таблицы и политики всегда читают настоящую модель, поэтому переименованный узел никогда не спутать с переименованием в модели.

Представления — доска, таблица, матрица, поток

Заголовок раздела «Представления — доска, таблица, матрица, поток»

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

(story не является представлением и не претендует на этот слот — это упорядоченный обход того, что проекция уже рисует, поэтому она сочетается с любым из них. См. Главу 36.)

view ProcessAudit {
show process and in Corporate.Loans
table {
column @name
column @@team "Owner"
column "PII": exists ((* > PiiServices) and in this)
sort by @name
}
}

Столбец-геттер (column @@team "Owner") редактируемый — правка ячейки записывает поле или аспект обратно в модель. Именованный вычисляемый столбец (column "PII": <expr>) — только для чтения.

Матрица структуры зависимостей (DSM) над выборкой. Необязательное тело задаёт оси геттерами — matrix { rows @@team; cols @@team } — иначе обе оси суть показанные элементы. Заполненная ячейка — это рёбра-зависимости между её строкой и столбцом; клик по пустой ячейке создаёт черновое ребро.

flow <ProcessRef> рендерит процесс. Субъект записывается явно, а не выводится:

view CheckoutWalkthrough {
"Checkout for the onboarding deck — PII touchpoints highlighted"
flow Checkout
style * > PiiServices { color: crimson }
}

У проекции-потока три режима, задаваемых токеном после процесса:

  • plain (без токена) — пошаговый обход потока.
  • sequence — диаграмма последовательности в стиле UML с линиями жизни.
  • bpmn — BPMN-коллаборация с дорожками (swimlanes).

Режим — это истина представления, поэтому кураторский обход поставляется в задуманном режиме. Режим bpmn принимает необязательное тело в фигурных скобках, которое сортирует исполнителей по дорожкам (lanes) и группирует дорожки в пулы (pools):

view CheckoutOps {
"Checkout as a BPMN collaboration, laned by team"
flow Checkout bpmn {
lane by @@team // по одной дорожке на каждое значение команды
pool by @@domain // сгруппировать дорожки в пулы по домену
}
}

В ArchLang нет встроенной «сетевой плоскости» или «плоскости безопасности». Плоскости возникают из аспектов, которые вы объявляете на модулях, и из того, по чему группирует проекция.

// Network plane — group by network segment.
view NetworkDiagram {
group by @@network.segment
}
// Business plane — group by business entity.
view BusinessDomains {
group by @@business.entity
}
// Security plane — PCI scope, grouped by zone.
view PCIScope {
show @@security.zone:"PCI"
group by @@security.zone
}

Одна модель, три проекции, три аудитории. Аспекты делают работу; проекции выносят срезы на поверхность.

Именно так инфраструктура остаётся в стороне. Вы не рисуете ServiceA → broker → ServiceB; вы рисуете логический вызов и вешаете брокер на аспект (Глава 9). Проекция, которая показывает или группирует по этому аспекту, включает брокер в поле зрения, когда кто-то спрашивает «какие связи через какой брокер идут», и оставляет его за пределами каждой другой диаграммы. Клауза on делает это явным — on deployment задаёт контекст плоскости для всей доски. Смоделируйте систему один раз на бизнес-уровне; раскрывайте каждую инфраструктурную плоскость по требованию.

Проекция может принимать ручки (knob) — именованные параметры, привязываемые при открытии или экземпляром:

view ZoneCompliance {
"One security zone and its egress; pick the zone, dial the radius"
knob zone from @@security.zone
knob depth: 1 { min: 1; max: 3 }
show @@security.zone:$zone or nodes of (@@security.zone:$zone <> * within $depth)
}
view ZoneCompliance PciProd { // экземпляр: привязывает ручку, добавляет клаузу
zone: "PCI"
hide database
}

$zone читает ручку; ZoneCompliance PciProd — сохранённый экземпляр, который поставляет зону, уже выставленную на "PCI".

  • Изменять модель. Добавление узла в проекцию не добавляет его в архитектуру. Проекции — только чтение канонического состояния; переопределения стиля и переименования применяются только при рендеринге.
  • Определять новую структуру. Проекция не может нарисовать синтетическое ребро. Если вам нужно ребро, объявите шаг процесса.
  • Свободно переиспользовать содержимое другой проекции. Общего extends для проекций нет — единственный путь композиции — это экземпляр проекции, который связывает параметры (knob) и добавляет клаузы (см. выше). Общий словарь — задача запроса (export query).

Проекции несут стабильные идентификаторы так же, как модули и процессы. Форматтер выдаёт #v42 при сохранении. Переименование проекции обнаруживается как переименование, а не удаление-плюс-добавление.

  • Проекция — это сохранённая линза над моделью: show/hide/focus, group by, style и одно представление (table, matrix или flow) поверх доски по умолчанию.
  • Селекторы делают выбор; геттеры снабжены сигилами (@@team — аспект, @name — поле).
  • style — единственный механизм переопределения: цвет (через явное приведение, питающее легенду), pin, rename, свойства виджетов и визуал violating <Policy>.
  • Клаузы layout нет; раскладка — забота солвера, позиции закрепляются поузлово.
  • Плоскости (сетевая, безопасности, бизнес) возникают из аспектов плюс проекций, а не из отдельной онтологии.

Глава 9: Поля и аспекты → — слой значений, на котором работают аспекты и проекции.