[Перевод] Structurizr, описание, перевод (часть 1/3)
Введение
В данной статье содержится вольный перевод документации по Structurizr.
Из-за большого объема информация разделена на три статьи:
Пример
Следующий DSL может быть использован для описания простейшей модели архитектуры программного обеспечения: пользователя, использующего программную систему.
workspace {
model {
user = person "User"
softwareSystem = softwareSystem "Software System"
user -> softwareSystem "Uses"
}
views {
systemContext softwareSystem {
include *
autolayout lr
}
}
}
Данному коду соответствует следующая диаграмма:
Основы
Правила DSL
переносы строк учитываются: длинные строки можно разделять символом »\»;
строки обрабатываются по порядку;
токены должны быть разделены пробелами, но количество пробелов и отступов не имеет значения;
ключевые слова не чувствительны к регистру;
двойные кавычки необязательны, если свойство/выражение не содержит пробелов;
открывающая фигурная скобка »{» должна находиться в одной строке с выражением (то есть в последнем символе инструкции, а не в отдельной строке);
закрывающая фигурная скобка »}» должна располагаться на отдельной строке;
открывающие и закрывающие фигурные скобки требуются только при добавлении дочернего содержимого;
используйте » в качестве значения по умолчанию для необязательных свойств;
теги разделяются запятыми.
Правила рабочего пространства
На рабочие пространства распространяются следующие правила:
представление имеет уникальный «ключ» (генерируется, если не указан; автоматически сгенерированные ключи не гарантируют стабильности с течением времени);
наименования ПО и имена сотрудников должны быть уникальными;
наименования контейнеров должны быть уникальными в контексте программной системы;
наименования компонентов должны быть уникальными в контексте контейнера;
наименования узлов развертывания должны быть уникальными для их родительского контекста;
наименования узлов инфраструктуры должны быть уникальными для их родительского контекста;
все связи от исходного элемента к целевому элементу должны иметь уникальное описание.
Подразумеваемые связи
!impliedRelationships
Ключевое слово ! impliedRelationships предоставляет способ включить или отключить создание подразумеваемых связей. Значение false отключает создание подразумеваемых отношений, в то время как true создает подразумеваемые отношения между всеми допустимыми комбинациями родительских элементов, если только между ними уже не существует каких-либо отношений.
Константы
Ключевое слово ! constant используется для определения константы. Константы нужны для выполнения подстановок (описание ниже).
!constant
Имена констант должны состоять из символов: a-zA-Z0–9-_.
Подстановки
Подстановки производятся в любом тексте, указанном в токене, используя синтаксис ${NAME}, где NAME — наименование константы или переменной окружения.
!constant ORGANISATION_NAME "Organisation"
!constant GROUP_NAME "Group"
workspace {
model {
group "${ORGANISATION_NAME} - ${GROUP_NAME}" {
user = person "User"
}
}
}
Если соответствующая константа или переменная окружения не найдены, подстановка не будет выполнена.
Комментарии
/*
multi-line comment
*/
/* single-line comment */
# single line comment
// single line comment
Значения по умолчанию
DSL спроектирован максимально компактным. При использовании в сочетании со Structurizr Lite или Structurizr CLI для следующего фрагмента:
будет создана подразумеваемая взаимосвязь между
user
иsoftwareSystem
;создан набор представлений по умолчанию;
добавлены несколько стилей элементов по умолчанию из темы.
workspace {
model {
user = person "User"
softwareSystem = softwareSystem "Software System" {
webapp = container "Web Application"
database = container "Database"
}
user -> webapp "Uses"
webapp -> database "Reads from and writes to"
}
views {
theme default
}
}
Идентификаторы
По умолчанию все элементы и связи анонимны, на них нельзя ссылаться из DSL. Например, следующие инструкции создадут пользователя и программную систему, но ни на одну из них невозможно сделать ссылку:
person "User"
softwareSystem "Software System"
Чтобы создать связь между двумя элементами, нужна возможность ссылаться на них. Это можно сделать, определив идентификаторы таким же образом, как вы бы определили переменную в других языках программирования:
p = person "User"
ss = softwareSystem "Software System"
Теперь можно использовать эти идентификаторы при создании связей, указывая, какие элементы следует включать/исключать из представлений и так далее.
p -> ss "Uses"
Связи также может быть присвоен отдельный идентификатор:
rel = p -> ss "Uses"
Идентификаторы нужны там, где планируются ссылки на элемент/связь.
При определении идентификатора можно использовать символы: a-zA-Z_0–9.
Область действия идентификатора
По умолчанию все идентификаторы имеют глобальную область действия. Следующей код завершится ошибкой, указывающей, что идентификатор «api» уже используется:
workspace {
model {
softwareSystem1 = softwareSystem "Software System 1" {
api = container "API"
}
softwareSystem2 = softwareSystem "Software System 2" {
api = container "API"
}
}
}
Ключевое слово ! identifiers указывает, что идентификаторы элементов должны обрабатываться, как иерархические. Этот параметр не влияет на идентификаторы отношений. Например:
workspace {
!identifiers hierarchical
model {
softwareSystem1 = softwareSystem "Software System 1" {
api = container "API"
}
softwareSystem2 = softwareSystem "Software System 2" {
api = container "API"
}
}
}
Теперь на два контейнера API можно ссылаться через идентификаторы softwareSystem1.api и softwareSystem2.api соответственно.
Выражения
Structurizr DSL поддерживает «выражения» для использования при включении или исключении элементов/связей в представлениях (за исключением динамических представлений). Выражения следует заключать в кавычки, если они содержат пробелы. Например:
include "element.tag==Tag 1" // (correct)
include element.tag=="Tag 1" // (incorrect)
Выражения для элементов
-> // указанный элемент и принимающий элемент
-> // указанный элемент и передающий элемент
->-> // указанный элемент плюс передающий и принимающий элемент
element.type== // элемент указанного типа
// (Person, SoftwareSystem, Container, Component, DeploymentNode,
// InfrastructureNode, SoftwareSystemInstance, ContainerInstance, Custom)
element.parent== // элемент с указанным родительским элементом
element.tag==[,tag] // все элементы, имющие все указанные теги
element.tag!=[,tag] // все элементы, которые не имеют всех указанных тегов
element==-> // указанный элемент и приниающая связь
element==-> // указанный элемент и передающая связь
element==->-> // указанный элемент плюс передающая и принимающая связь
Выражения для связей
*-> // все связи
->* // все связи с указанным исходным элементом
*-> // все связи с указанным целевым элементом
relationship==* // все связи
relationship==*->*: // все связи
relationship.tag==[,tag] // связи, имеющие все указанные теги
relationship.tag!=[,tag] // все связи, которые не имеют всех указанных тегов
relationship.source== // все связи с указанным исходным элементом
relationship.destination== // все связи с указанным целевым элементом
relationship==->* // все связи с указанным исходным элементом
relationship==*-> // все связи с указанным целевым элементом
relationship==-> // все связи между двумя указанными элементами
Включение файлов
Ключевое слово ! include используется для включения одного или нескольких файлов, обеспечения модульности и повторного использования некоторых фрагментов между рабочими пространствами. Содержимое любых включенных файлов просто встраивается в родительский документ в том порядке, в котором файлы были обнаружены.
!include
Для включения можно использовать:
file: отдельный локальный файл с относительным путем, расположенный в том же каталоге, что и родительский файл, или в его подкаталоге;
directory: локальный каталог, содержащий один или несколько файлов DSL, с относительным путем, расположенный в том же каталоге, что и родительский файл, или в его подкаталоге;
url: URL-адрес (HTTPS), указывающий на один DSL-файл.
Примеры использования:
!include people.dsl
!include model/people.dsl
!include model
!include https://example.com/model/people.dsl
Документация
Ключевое слово ! docs используется для присоединения документации Markdown/AsciiDoc к родительскому контексту (рабочей области, программной системе или контейнеру).
!docs [fully qualified class name]
Путь должен быть относительным, расположен в том же каталоге, что и родительский файл, или в его подкаталоге. Например:
!docs subdirectory
Класс по умолчанию использоваться для импорта документации следующим образом:
Все файлы Markdown и AsciiDoc в указанном каталоге будут импортированы в алфавитном порядке в соответствии с именем файла.
Все изображения в данном каталоге (и подкаталогах) также импортируются в рабочую область.
Заголовки разделов и нумерация обрабатываются особым образом.
Описанное выше поведение можно настроить, указав полное имя класса вашей собственной реализации DocumentationImporter, которое должно быть указано в пути к классу DSL или установлено в виде JAR-файла в каталоге plugins рядом с вашим DSL-файлом.
Архитектурные решения (ADR)
Ключевое слово ! adrs используется для присоединения Markdown/AsciiDoc ADR к родительскому контексту (рабочей области, программной системе или контейнеру).
!adrs [fully qualified class name]
Путь должен быть относительным, расположенным в том же каталоге, что и родительский файл, или в его подкаталоге. Например:
!adrs subdirectory
Класс по умолчанию использоваться для импорта ADR следующим образом:
Все файлы Markdown в этом каталоге будут импортированы в алфавитном порядке в соответствии с именем файла.
Файлы должны быть созданы с помощью adr-tools или, по крайней мере, соответствовать тому же формату.
Все изображения в данном каталоге (и подкаталогах) также импортируются в рабочую область.
Описанное выше поведение можно настроить, указав полное имя класса вашей собственной реализации DocumentationImporter, которое должно быть указано в пути к классу DSL или установлено в виде JAR-файла в каталоге plugins рядом с вашим DSL-файлом.
Скрипты
Скрипты похожи на плагины, но их не нужно компилировать перед использованием. JavaScript (Nashorn движок JavaScript JVM считается устаревшим), Kotlin, Groovy и Ruby поддерживаются «из коробки», и вы можете добавить дополнительные языки с помощью Java Scripting API. Скрипты можно использовать в любой точке DSL.
Следующие переменные доступны из скриптов:
context;
workspace;
element: объект текущего элемента, если сценарий используется в рамках представления элемента;
relationship: объект текущего отношения, если сценарий используется в рамках представления отношения;
view.
Встроенные скрипты
Для подключения скрипта используется ключевое слово ! script, за которым следует выбранный язык (groovy, kotlin, ruby или javascript). Например, следующий скрипт Groovy создаст набор представлений по умолчанию без включенной автоматической компоновки:
!script groovy {
workspace.views.createDefaultViews()
workspace.views.views.findAll { it instanceof com.structurizr.view.ModelView }.each { it.disableAutomaticLayout() }
}
Встроенные скрипты не могут содержать строку, содержащую только закрывающий символ }.
Внешние скрипты
Чтобы использовать внешний скрипт, создайте файл скрипта рядом с DSL-файлом (например, script.kts):
workspace.views.createDefaultViews()
workspace.views.views.forEach { it.disableAutomaticLayout() }
Затем можно подключить скрипт, используя ключевое слово ! script:
!script script.kts
Поддерживаемые расширения файлов:
.groovy
(Groovy),.kts
(Kotlin),.rb
(Ruby),.js
(JavaScript).
Параметры для внешних скриптов можно задать следующим образом:
!script script.kts {
name value
}
Плагины
Плагины используются там, где требуется больший контроль или настройка, и обеспечивают доступ к рабочей области через библиотеку Structurizr для Java. Например, можно использовать плагин для создания элементов модели на основе внешнего источника данных или определять представления программно. Плагины могут быть использованы в любой точке DSL.
Чтобы написать плагин, создайте класс Java, который реализует интерфейс com.structurizr.dsl.StructurizrDslPlugin (вам нужно будет добавить зависимость от библиотеки DSL, которую можно найти в Maven Central через com.structurizr: structurizr-dsl).
package com.example;
import com.structurizr.Workspace;
public class TestPlugin implements StructurizrDslPlugin {
@Override
public void run(StructurizrDslPluginContext context) {
Workspace workspace = context.getWorkspace();
workspace.setName("Name set by plugin");
}
}
Скомпилированный плагин, упакованный в виде JAR-файла (плюс любые другие зависимости JAR), должен быть помещен в каталог с именем plugins рядом с вашим DSL-файлом. Затем вы можете использовать свой плагин из DSL, с помощью ключевого слова ! plugin.
workspace {
!plugin com.example.TestPlugin
}
Параметры могут быть указаны, например, в теле плагина:
workspace {
!plugin com.example.TestPlugin {
name value
}
}
Именованные параметры становятся доступными с помощью метода getParameter (name) объекта StructurizrDslPluginContextobject.