Диаграмма Эффектов: Спецификация v0.0.2

May 19, 2022

Это первый пост в серии, посвящённый диаграмме эффектов:

  1. "Спецификация": назначение диаграммы, основные концептуальные элементы и их визуальное представление
  2. "Пример построения диаграммы, проект True Story Project (TSP)" - процесс построения диаграммы эффектов реального проекта
  3. "Методика объектно-ориентированной декомпозиции" - рациональный подход к разбиению системы на модули с помощью диаграммы эффектов и его применение для декомпозиции проекта TSP
  4. "Методика перевода диаграммы в код" - процесс трансляции диаграммы в исходный код на примере проекта TSP

Введение

Идентичность информационной системы определяется тем, как она взаимодействует с внешним миром. Что, где и когда она сохраняет и запрашивает; что, куда и когда отправляет. Это я называю наблюдаемым поведением.

Можно переписать приложение с Java на Haskell, сменить слоёную архитектуру на шестиугольную, реляционную базу данных заменить документной, а пользовательский интерфейс перевести с серверной генерации HTML на React Native - если наблюдаемое поведение системы останется неизменным, то это будет просто очередная версия всё той же системы. Если же кардинально изменить её взаимодействие с внешним миром, то это будет уже другая система.

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

При всей значимости наблюдаемого поведения я не знаю ни одного общепринятого инструмента для его проектирования и визуализации. Поэтому изобрёл свой - диаграмму эффектов.

Концептуальная модель

В основе концептуальной модели диаграммы поведения лежат операции - атомарные единицы поведения системы. Операции характеризуются в первую очередь своими эффектами - взаимодействием с окружающим миром. Значимые элементы окружающего мира, с которыми могут взаимодействовать операции, представлены внутри системы ресурсами. Наконец, выполнение операций инициируется событиями. Рассмотрим эти элементы подробнее.

Операция

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

Эффект

Если не вдаваться в философию программирования, то эффект - это ввод-вывод. Включая запись и чтение глобальных переменных (кучи).

Очевидно, эффекты бывают двух видов - чтения и записи. Первые изменяют состояние программы, вторые - состояние окружающего мира.

Ресурс

У эффектов, которые являются действиями, есть объект - целевое состояние. Это какая-то часть физического мира, с которой будет взаимодействовать устройство ввода-вывода в процессе реализации эффекта. Чаще всего целевым состоянием выступают биты на носителях информации, но это могут быть и пиксели экрана, и динамик колонки и нога робота. Эти кусочки физического мира представлены в системе ресурсами.

Событие

Наконец, программы (особенно интерактивные) редко выполняют эффекты сплошным потоком - обычно программа выполняет набор эффектов в ответ на какое-то событие и засыпает до следующего события. Таким событием может быть получение вызова удалённой процедуры в любом виде, появление нового сообщения в некоторой очереди, наступление определённого момента времени или истечение определённого периода времени.


Чёткое понимание операций системы (когда они выполняются и к каким эффектам приводят) является критически важным на всех этапах жизненного цикла разработки системы.

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

На этапе проектирования операции и ресурсы становятся ключевыми блоками, правильная декомпозиция которых создаст основу для системы с низкой сцепленностью.

На этапе реализации сложность операций (определяемая количеством и типом обеспечивающих её ресурсов) и их зависимость через ресурсы помогают определить порядок выполнения работ и те работы, которые могут быть выполнены параллельно.

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

Реализация концептуальной модели в коде

Все описанные выше элементы транслируются непосредственно в код: события и операции - в методы, ресурсы - в классы, эффекты - в вызовы методов.

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

Ресурсы превращаются в структуру данных и коллекцию методов работы с ней - классы Spring Data агрегата и репозитория, классы события и ApplicationEventPublisher-а (или обёртки вокруг него), классы REST API модели и клиента и т.п. В контексте бэкэндов информационных систем, самыми распространёнными видами ресурсов являются:

  1. любые постоянные коллекции данных - таблицы в реляционной СУБД, коллекции в документной СУБД и т.д.
  2. REST API внешних сервисов
  3. любые очереди сообщений/шины событий
  4. изменяемые структуры данных, доступные через глобальные переменные

События превращаются в методы, передаваемые фреймворку для последующего вызова - метод Spring-ового RestController-а, Swing-овый EventListener, реализация Runnable для таймера и т.д. Если говорить о бакэндах информационных систем, то самыми распространёнными видами событий являются:

  1. Получение запроса по сети (@RestController + @*Mapping в случае разработки на Spring). Сейчас популярностью пользуется протокол запросов в REST-стиле, но SOAP, gRPC, CORBA и т.п. так же попадают в эту категорию.
  2. Появление сообщения в очереди (@JmsListener).
  3. Доменное событие или событие приложения (@EventListener)
  4. Наступление определённого момента времени (@Scheduled). Два основных типа таких событий:
    1. наступление заранее известного момента времени (например, полуночи вторника)
    2. истечение определённого времени с момента в прошлом (например, истечение суток с момента создания предыдущего бэкапа).

Нотация

Основу визуального языка диаграммы эффектов я позаимствовал в модели C4. Во-первых, мне нравится сам язык модели C4. А во-вторых, диаграмму эффектов можно встроить в модель C4 на четвёртом уровне - вместо кода. Кроме того, диаграмму третьего уровня (компонентов) я строю как раз на базе диаграммы эффектов.

Диаграмма эффектов бывает двух типов - краткая и полная. Краткая содержит только обозначение эффектов и связанных ими операций и ресурсов. Полная нотация дополнительно включает события и их источники, внешние системы, обеспечивающие реализацию ресурсов и более полное описание всех элементов.

Рассмотрим нотации с помощью минимального примера визуализации функциональности регистрации и аутентификации пользователей в произвольной системе. После успешной регистрации пользователям необходимо отправлять приветственное письмо. Начнём с краткой нотации.

Краткая нотация

В краткой нотации диаграмма выглядит следующим образом (картинка кликабельна):

short notation example

Теперь рассмотрим отдельные элементы

Операции

Операции обозначаются прямоугольником с именем операции:

operation

Ресурсы

Ресурсы обозначаются прямоугольником с именем ресурса и цветом, отличным от цвета операции:

resource

Эффекты

Эффект модификации ресурса обозначается "сильной" (более заметной) стрелкой от операции к ресурсу, с кратким описанием эффекта:

operation resource rw

Эффект чтения ресурса обозначается стрелкой от ресурса к операции, с кратким описанием считываемых данных:

operation resource ro

Эффекты вызова операций

Есть особый вид стрелок для эффектов вызова операций вследствие взаимодействия с ресурсами. Как правило, это ресурсы всевозможных шин событий и связанные с ними операции-обработчики. Такие связи отображаются прерывистой стрелкой того же стиля, что и стрелка эффекта записи с кратким описанием связи:

resource operation rw

Примечания

Также на диаграмму можно помещать заметки и примечания, используя любую удобную нотацию. Я предпочитаю нотацию UML - "лист" с загнутым углом, связанный прерывистой линией с комментируемым элементом.

note

Это все элементы, составляющие ядро диаграммы эффектов.

Полная нотация

Теперь рассмотрим ту же функциональность, описанную в полной нотации:

full notation example

В полной нотации появляются:

  1. события
  2. описание операций и ресурсов в формате модели C4
  3. границы контейнера из C4. Обозначает границы процесса - всё, что находится внутри этих границ выполняется в памяти визуализируемого приложения
  4. внешние системы, базы данных и компоненты из C4. Внешние системы могут быть как источником события, так и средством реализации ресурса

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

События

На мой взгляд, из дополнительных элементов наибольшую ценность имеют события. В полной нотации они обозначаются стрелкой от внешней системы к операции с кругом на стартовом конце и описанием в формате C4. Но в промежуточной версии, внешнюю систему можно опустить и "подвесить" стрелку:

event operation

Описания

Затем блоки операций и ресурсов можно дополнить типом, способом реализации и описанием:

descriptions

Внешние системы

Элементы, обозначающие границы системы и внешние системы полностью соответствуют нотации C4:

  1. Границы системы отображаются прерывистым прямоугольником приглушённого цвета и подписью с именем контейнера
  2. Управляемые внешние системы и базы данных обозначаются прямоугольником и символом "База Данных"
  3. Неуправляемые внешние системы и компоненты обозначаются приглушёнными прямоугольниками
  4. Неуправляемые базы данных обозначаются приглушённым символом "База Данных"

Внешние системы связываются с операциями посредством событий:

event sources

А ресурсы связываются с внешними системами посредством стрелок с описанием:

resource impls

Ресурс может быть связан со сторонним компонентом, работающем в том же процессе:

resource component

Здесь приведена связь ресурса с эффектом вызова операции системы. В случае же если ресурс не обладает таким эффектом, то он соединяется со сторонним компонентом обычной стрелкой.


Выбор нотации зависит от решаемой задачи. Если надо быстро разбить систему на модули, или спланировать модификацию сложной или незнакомой операции - можно обойтись краткой нотацией. Если надо оценить проект для работы за фиксированную цену - лучше взять полную нотацию, чтобы минимизировать вероятность "потери" существенных деталей.

Ещё два критерия выбора нотации - срок жизни диаграммы и размер целевой аудитории диаграммы. Если планируете выкинуть диаграмму после анализа и никому не будете её показывать - можно обойтись краткой нотацией. Если же вы планируете возвращаться сами к диаграмме через длительный срок или публиковать её для ознакомления без вашего руководства - стоит как минимум добавить события и описания ресурсов и операций.

Я сам обычно начинаю с промежуточной нотации - краткой с событиями, и дополняю её по мере необходимости.

Инструментарий

Одним из плюсов базирования на визуальном языке модели C4 является то, что для диаграммы эффектов можно использовать любой инструмент с поддержкой C4. А в силу простоты C4, таким инструментом может быть хоть графический редактор. Тем не менее поддержка привязки элементов сильно помогает, поэтому я сам сейчас использую десктопную версию draw.io.

Заключение

Сущность информационной системы заключена в её поведении - эффектах, которые она оказывает на окружающий мир.

Самым удобным способом отразить суть поведения системы является связка События-Эффекты - какими эффектами на какие ресурсы в ответ на какие события система реагирует.

Для визуализации, анализа и проектирования этой связки я разработал диаграмму эффектов. Предлагаю и вам взять её на вооружение и, надеюсь, что она поможет вам лучше понять душу ваших программ и точнее отразить её в эргономичном коде.

Но прежде чем приступить к визуализации поведения, его надо понять. В следующем посте я опишу процесс построения диаграммы эффектов (читай - понимания сути задачи) реального проекта.