8. Проекции
Модель одна. Аудиторий много. Платформенного инженера волнует топология сервисов; ревьюера по безопасности — какие сервисы касаются PCI-данных; продакт-менеджера — пользовательский поток. Всем им нужны разные диаграммы одной и той же базовой архитектуры.
Проекция — это сохранённая линза над моделью: что показать, как это представить, как это стилизовать. Проекции никогда не изменяют модель; они лишь выбирают, как её отобразить.
view PaymentsLandscape { "Payments domain, grouped by owning team" show @@domain:"Payments" hide database group by @@team}Читается сверху вниз: показать каждый элемент, несущий аспект domain: "Payments", скрыть базы данных, сгруппировать оставшееся по командам.
Три идеи пронизывают всё, что связано с проекциями:
- Доска — это проекция. Проекции — единственный путь рендеринга. Открытие модели без проекции рендерит синтезированную доску по умолчанию; открытие вкладки процесса рендерит синтезированную проекцию
flow. Нет второго конвейера, который мог бы разойтись с первым. - Проекции никогда не содержат истину. Проекция выбирает, представляет и перестилизовывает — она никогда не объявляет структуру, рёбра или аспекты. Всё это принадлежит модели. Переопределения стиля применяются только при рендеринге; каждый селектор и геттер читает настоящую модель под ними.
- Клаузы упорядочены. Тело проекции — это зона заголовка (описание,
knob-параметры), за которой следуют клаузы, читаемые сверху вниз: сначалаshow, затемhide, затемgroup, затемstyle. Ревьюер читает проекцию так же, как она вычисляется.
Зачем нужны проекции
Заголовок раздела «Зачем нужны проекции»Без проекций у вас одна диаграмма: каждый модуль, каждый интерфейс, каждая стрелка процесса — всё сразу. Для всего, что больше крошечной системы, такая диаграмма нечитаема.
Проекции позволяют делать срезы:
- Показать только домен Payments.
- Сгруппировать архитектуру по командам для оргсхемы системы.
- Скрыть всё за пределами PCI-зоны для ревью безопасности.
- Раскрасить каждый сервис по зоне, в которой он живёт, и отметить рёбра, нарушающие политику.
Каждая проекция — это сохранённый запрос. Получатели открывают тот же URL проекции и видят ту же диаграмму.
Выборка — show, hide, focus
Заголовок раздела «Выборка — show, hide, focus»Каждая клауза выборки принимает селектор — тот же небольшой язык запросов, что используется везде в 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
Заголовок раздела «Группировка — group by»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 — представление процесса
Заголовок раздела «flow — представление процесса»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 — параметризованные проекции
Заголовок раздела «Ручки-knob — параметризованные проекции»Проекция может принимать ручки (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: Поля и аспекты → — слой значений, на котором работают аспекты и проекции.