[Перевод] Structurizr, описание, перевод (часть 1/3)

Введение

В данной статье содержится вольный перевод документации по Structurizr.

Из-за большого объема информация разделена на три статьи:

Пример

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

workspace {

    model {
        user = person "User"
        softwareSystem = softwareSystem "Software System"

        user -> softwareSystem "Uses"
    }

    views {
        systemContext softwareSystem {
            include *
            autolayout lr
        }
    }
    
}

Данному коду соответствует следующая диаграмма:

531362cde8a43beb2a9b0a016213f8ae.png

Основы

Правила 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

Класс по умолчанию использоваться для импорта документации следующим образом:

  1. Все файлы Markdown и AsciiDoc в указанном каталоге будут импортированы в алфавитном порядке в соответствии с именем файла.

  2. Все изображения в данном каталоге (и подкаталогах) также импортируются в рабочую область.

  3. Заголовки разделов и нумерация обрабатываются особым образом.

Описанное выше поведение можно настроить, указав полное имя класса вашей собственной реализации DocumentationImporter, которое должно быть указано в пути к классу DSL или установлено в виде JAR-файла в каталоге plugins рядом с вашим DSL-файлом.

Архитектурные решения (ADR)

Ключевое слово ! adrs используется для присоединения Markdown/AsciiDoc ADR к родительскому контексту (рабочей области, программной системе или контейнеру).

!adrs  [fully qualified class name]

Путь должен быть относительным, расположенным в том же каталоге, что и родительский файл, или в его подкаталоге. Например:

!adrs subdirectory

Класс по умолчанию использоваться для импорта ADR следующим образом:

  1. Все файлы Markdown в этом каталоге будут импортированы в алфавитном порядке в соответствии с именем файла.

  2. Файлы должны быть созданы с помощью adr-tools или, по крайней мере, соответствовать тому же формату.

  3. Все изображения в данном каталоге (и подкаталогах) также импортируются в рабочую область.

Описанное выше поведение можно настроить, указав полное имя класса вашей собственной реализации 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.

© Habrahabr.ru