Event Storming: моделируем через события
Как Альберто Брандолини предлагает моделировать предметную область вместе с экспертами: доменные события на «бесконечной стене», три формата сессии, нотация стикеров, проведение офлайн и онлайн и ошибки фасилитатора. Разбираем сессию «записаться на поток и оплатить», закрываем открытые вопросы глоссария и переходим от стены к контекстам и агрегатам.
Зачем это нужно
Заголовок раздела «Зачем это нужно»Команда онлайн-школы два месяца писала требования к записи и оплате. Получилось тридцать страниц, эксперты их прочитали и согласовали. После запуска первый же звонок в поддержку: студент оплатил счёт через пять минут после того, как его запись автоматически отменилась. Деньги списаны, места нет, что делать — не знает никто. Вопрос не задали ни на одной из тридцати страниц, потому что текст читают по разделам, а такие случаи живут на стыке двух разделов — «оплата» и «отмена».
Такие стыки хорошо видны, если выложить процесс по времени: вот событие, вот следующее, а что, если между ними случится третье? На этом и построен метод Event StormingEvent Storming («штурм событий»)EN EventStormingМетод совместного моделирования Альберто Брандолини: эксперты и разработчики выкладывают доменные события на длинной ленте вдоль времени, а потом добавляют команды, акторов, политики, системы и горячие точки. Форматы: Big Picture, Process Level, Design Level. («штурм событий»), который придумал Альберто Брандолини. Эксперты и разработчики стоят у длинной бумажной ленты и выкладывают на неё факты предметной области, а пробелы и противоречия всплывают сами. В уроке DDD.1.2 в глоссарии школы остались три открытых вопроса — в этом уроке мы закроем их на стене. А в уроке DDD.1.3 было обещано, что события на стене сами соберутся в группы, — посмотрим, как это происходит и как из стикеров вырастают контексты и агрегаты.
Что такое Event Storming
Заголовок раздела «Что такое Event Storming»Event Storming — метод совместного моделирования. Его основа простая: «бесконечная» стена (рулон бумаги на несколько метров), время идёт слева направо, а на стену клеят оранжевые стикеры — доменные событиядоменное событиеEN domain eventЗначимый для экспертов факт, который уже произошёл, в прошедшем времени: «Студент записан», «Платёж получен». В Event Storming — оранжевый стикер, в коде — неизменяемый объект (урок DDD.3.3).. Шпаргалка DDD Crew определяет событие так: факт, значимый для экспертов предметной области в исследуемой области, глагол в прошедшем времени. Параллельные потоки событий располагают друг под другом.
Почему именно события, а не, скажем, сущности или экраны?
- Их понимают все. Бизнес рассказывает истории фактами: «студент записался, через двое суток не оплатил, место отдали следующему». Для этого не нужно знать UML или базы данных.
- Прошедшее время дисциплинирует. «Платёж получен» — это либо случилось, либо нет. Расплывчатая «оплата» на оранжевый стикер не помещается: её приходится уточнять — счёт выставлен? платёж получен? деньги возвращены?
- Порядок вскрывает пробелы. Когда события стоят в ряд, видно, где между ними пусто, где два отдела рассказывают одну историю по-разному и где никто не знает, что происходит дальше.
Сам Брандолини, как пересказывает шпаргалка DDD Crew, сравнивает Event Storming с пиццей: рулон бумаги и события — это тесто, а остальные ингредиенты кладут по вкусу и по задаче. Ингредиенты — это стикеры других цветов и форматы сессии.
Три формата
Заголовок раздела «Три формата»| Big Picture | Process Level | Design Level | |
|---|---|---|---|
| Цель | увидеть бизнес целиком: здоровье направления, проблемы, возможности, границы контекстов | разобрать один процесс: узкие места, правила, кто что решает | спроектировать код внутри одного контекста |
| Кто в комнате | много людей из разных отделов: 10–30 и больше | эксперты процесса и разработчики | разработчики и эксперты одного контекста |
| Масштаб | весь бизнес, от «курс опубликован» до «сертификат выдан» | один процесс: «записаться на поток и оплатить» | один агрегат или несколько |
| Стикеры | события, горячие точки, акторы, системы, возможности, ценность | вся «грамматика»: команды, политики, модели чтения | плюс агрегаты и их правила |
| Результат | общая картина, главная проблема, кандидаты в контексты | согласованный процесс, закрытые вопросы | кандидаты в агрегаты, их команды, события и инварианты |
В шпаргалке DDD Crew второй и третий форматы называются Process Modelling и Software Design; в русских и английских текстах встречаются и названия Process Level и Design Level. Форматы не обязательно проводить все и подряд: часто достаточно одного Big Picture на старте проекта и нескольких Process Level по самым болезненным процессам.
Нотация стикеров
Заголовок раздела «Нотация стикеров»Цвета ниже — «официальные» по шпаргалке DDD Crew. Команды нередко заменяют их тем, что есть под рукой; важно не это, а легенда на виду: лист с образцом каждого стикера висит на стене всю сессию.
| Стикер | Цвет | Что означает | Пример в онлайн-школе |
|---|---|---|---|
| Доменное событие | оранжевый | факт в прошедшем времени | «Студент записан», «Платёж получен» |
| Горячая точка | ярко-розовый, повёрнут ромбом | вопрос, конфликт, противоречие в языке, боль, «обсудим позже» | «Платёж пришёл после отмены — что делаем?» |
| Актор | маленький жёлтый | человек, роль или отдел, который действует | «Студент», «Менеджер набора» |
| Система | широкий розовый | внешняя или своя система, участвующая в процессе | «Платёжная система», «Сервис рассылок» |
| Команда | синий | намерение, решение, действие | «Записаться на поток», «Выставить счёт» |
| Политика | сиреневый | реакция «когда X — делаем Y» | «Когда платёж получен — подтвердить запись» |
| Модель чтения | зелёный | информация, которая нужна для решения | «Свободные места на потоках» |
| Агрегат (ограничение) | большой жёлтый | то, что принимает решение и защищает правила | «Поток» |
| Возможность | зелёный | идея улучшения (в Big Picture, в пару к горячим точкам) | «Предлагать место из листа ожидания автоматически» |
Пояснения к самым важным стикерам:
- Горячая точкагорячая точкаEN hotspotСтикер Event Storming (ярко-розовый, повёрнутый ромбом) для вопросов, конфликтов, противоречий в языке, болевых мест и всего, что решили обсудить позже. Фасилитатор ставит его вместо затянувшегося спора. — главный инструмент фасилитатора. Шпаргалка DDD Crew перечисляет, что она ловит: противоречия в языке, трения, вопросы, несогласие, возражения, проблемы и всё, что решили обсудить позже. Её ставят на месте спора и идут дальше.
- Командакоманда (в Event Storming)EN commandНамерение или решение что-то сделать: «Записаться на поток». Синий стикер. Её отдаёт актор или политика, итог — событие или отказ. — то, что кто-то решил сделать. В разговоре с бизнесом DDD Crew советует говорить «действие»: это слово понятнее. Итог команды — событие или отказ.
- Политикаполитика (в Event Storming)EN policyРеакция «когда случилось X — делаем Y» между событием и командой: «Когда платёж получен — подтвердить запись». Сиреневый стикер. Бывает автоматической и ручной. — правило-реакция между событием и следующей командой. Бывает автоматической («через 48 часов без платежа отменить запись») и ручной («когда пришла жалоба, менеджер связывается со студентом»). Шпаргалка замечает, что за политикой всегда скрывается больше, чем кажется вначале.
- Модель чтениямодель чтенияEN read model, query modelДанные, которые нужны человеку или программе, чтобы принять решение: «свободные места на потоках». В Event Storming — зелёный стикер; в CQRS — отдельная модель для чтения (урок DDD.4.1). — данные, на которые смотрит актор, прежде чем отдать команду: студент смотрит на свободные места, прежде чем записаться.
- Большой жёлтый стикер раньше называли агрегатом. Шпаргалка DDD Crew теперь называет его «ограничением» (constraint) — правилом, которое нужно соблюсти, чтобы выполнить команду, — а слово «агрегат» считает устаревшим для Event Storming: с бизнесом его лучше не произносить. Разработчики при этом по-прежнему видят в нём кандидата в агрегатагрегатEN aggregateКластер сущностей и объектов-значений, который меняется только целиком через корень и после каждой операции соблюдает свои инварианты. Граница агрегата — граница транзакции: одна транзакция меняет один агрегат. В онлайн-школе — поток с записями и листом ожидания. — подробно о них в уроке DDD.3.2.
Стикеры складываются в «грамматику», которую часто называют картинкой, объясняющей (почти) всё:
flowchart LR
R[/"модель чтения"/] --> A(["актор"])
A --> C["команда"]
C --> G[["агрегат или<br/>ограничение"]]
C --> S[("внешняя<br/>система")]
G --> E("событие")
S --> E
E --> P{{"политика"}}
P --> C2["следующая<br/>команда"]Big Picture онлайн-школы
Заголовок раздела «Big Picture онлайн-школы»Представим первую сессию в школе. В комнате менеджер набора, методист, бухгалтер, маркетолог, старший сотрудник поддержки, руководитель школы и четверо разработчиков. Шпаргалка DDD Crew описывает ход сессии так.
- Хаотичное исследование. Каждый молча пишет события, которые знает, и клеит их туда, где, как ему кажется, они происходят. Молчание важно: участники не должны влиять друг на друга. Фасилитатор не торопит и не отвечает на вопросы — это самая важная часть сессии.
- Выстраиваем хронологию. Теперь можно говорить. Участники упорядочивают события, обсуждают и убирают дубликаты. Осторожно с дубликатами: два одинаковых стикера могут оказаться одним словом для разных понятий. Так на стене школы рядом оказались «Оплата прошла» маркетолога и «Счёт оплачен» бухгалтера — и выяснилось, что маркетолог имел в виду момент, когда студент нажал кнопку, а деньги приходят позже. Структуру — дорожки, вертикальные линии — добавляют аккуратно: слишком ранний порядок прячет ценные находки.
- Горячие точки. Конфликты при выстраивании хронологии — это хорошо: из них растёт понимание. Фасилитатор отмечает их розовыми стикерами, не давая спорам затянуться.
- Новые понятия — по мере надобности. Когда участникам понадобятся акторы, системы или возможности, их добавляют в легенду и объясняют.
Дальше обычно ищут ключевые событияключевое событиеEN pivotal eventОдно из немногих самых важных событий процесса, после которого многое меняется и которое интересно многим участникам. Ключевые события делят стену Event Storming на части и подсказывают границы контекстов. — немногие самые важные факты, после которых многое меняется и которыми интересуются многие участники. Для интернет-магазина шпаргалка приводит пример: «товар добавлен в каталог», «заказ оформлен», «заказ отправлен», «платёж получен», «заказ доставлен». Удобный приём проверки — рассказ вдоль стены: один участник ведёт историю слева направо, остальные останавливают его на каждом пробеле. Полезно пройти стену и в обратную сторону, от последнего события к первому, задавая вопрос «что должно было случиться перед этим?». Завершается Big Picture выбором главной проблемы: участники голосуют за горячие точки, и следующая сессия посвящается победителю.
У школы стена после выстраивания хронологии выглядит примерно так:
flowchart LR
subgraph K["Каталог"]
e1("Курс опубликован") --> e2("Поток открыт<br/>для записи")
end
subgraph N["Набор"]
e3("Студент записан") --> e6("Запись подтверждена")
e3 --> e11("Запись отменена") --> e12("Место освободилось")
end
subgraph P["Оплата"]
e4("Счёт выставлен") --> e5("Платёж получен")
end
subgraph L["Обучение"]
e7("Поток стартовал") --> e8("Задание сдано") --> e9("Курс пройден")
end
subgraph C["Сертификация"]
e10("Сертификат выдан")
end
e2 ==> e3
e3 --> e4
e5 --> e6
e6 ==> e7
e9 ==> e10Посмотрите, как на стене проявились контексты. Ключевые события — «Поток открыт для записи», «Запись подтверждена», «Поток стартовал», «Курс пройден», «Сертификат выдан» — делят ленту на участки. Возле каждого участка стоят свои люди: у записи — менеджер набора, у счетов — бухгалтер, у заданий — методист. Меняется язык: слева говорят «поток» и «место», в середине — «счёт» и «платёж», справа — «задание» и «прогресс». Шпаргалка DDD Crew называет такие группы возникающими ограниченными контекстами (emerging bounded contexts): это первые подсказки, где проектировать границы. Для школы они подтвердили шесть контекстов из урока DDD.2.1 — а Уведомления нашлись по-другому: их событие «Письмо отправлено» висело почти под каждым участком, и его вынесли в отдельную дорожку.
Process Level: «записаться на поток и оплатить»
Заголовок раздела «Process Level: «записаться на поток и оплатить»»Голосование на Big Picture выбрало главную боль — «запись и оплата». На вторую сессию пришли менеджер набора, бухгалтер, сотрудник поддержки, двое разработчиков и фасилитатор. Задача — разобрать процесс полной грамматикой и закрыть вопросы.
Сессия идёт по тем же правилам, но теперь к каждому событию спрашивают: что его вызвало? (команда — от кого? или внешняя система?) и что происходит после? (какая политика срабатывает?). Вот что получилось на основной ветке и двух болезненных ответвлениях:
flowchart TB
A1(["Студент"]) --> C1["Записаться на поток"]
R1[/"Свободные места на потоках"/] --> A1
C1 --> G1[["Поток"]] --> E1("Студент записан")
E1 --> P1{{"Когда студент записан —<br/>выставить счёт"}} --> C2["Выставить счёт"] --> G2[["Счёт"]] --> E2("Счёт выставлен")
E2 --> A2(["Студент"]) --> C3["Оплатить счёт"] --> S1[("Платёжная система")] --> E3("Платёж получен")
E3 --> P2{{"Когда платёж получен —<br/>подтвердить запись"}} --> C4["Подтвердить запись"] --> G3[["Поток"]] --> E4("Запись подтверждена")
E1 --> T1{{"Через 48 часов без платежа —<br/>отменить запись"}} --> C5["Отменить неоплаченную запись"] --> G4[["Поток"]] --> E5("Запись отменена<br/>Место освободилось")
E3 --> P3{{"Когда платёж получен,<br/>а запись отменена — вернуть платёж"}} --> C6["Вернуть платёж"] --> G5[["Счёт"]] --> E6("Платёж возвращён")
H1>"Горячая точка: платёж пришёл<br/>после отмены — что делаем?"] -.-> P3Горячие точки и решения экспертов
Заголовок раздела «Горячие точки и решения экспертов»Самое ценное в сессии — горячие точки. Три из них — открытые вопросы глоссария из урока DDD.1.2; эксперты школы закрыли их прямо у стены. Решения ниже — условия учебного примера, как и числа 48 часов, 7 суток и 24 часа.
| Горячая точка | Решение экспертов | Что появилось на стене и в глоссарии |
|---|---|---|
| Можно ли снова записаться на поток после отмены? | Да, если запись на поток ещё открыта и есть свободное место. Правило «не дважды» касается действующих записей — «ожидает оплаты» и «подтверждена». Отменённая запись остаётся в истории, повторная запись — новая запись | уточнённое правило 2 Набора: у студента не может быть двух действующих записей на один поток |
| Платёж пришёл после автоматической отмены — что делать? | Вернуть платёж полностью. Запись не восстанавливается: место могло уже уйти первому в листе ожидания. Если места есть, студент может записаться снова по общим правилам | политика «Когда платёж получен, а запись отменена — вернуть платёж»; новое событие ПлатёжВозвращён (PaymentRefunded) |
| Что подтверждает студент из листа ожидания за 24 часа? | Согласие занять предложенное место. Пока идут 24 часа, место закреплено за ним. После подтверждения появляется обычная запись «ожидает оплаты» с теми же 48 часами на оплату. Нет ответа за 24 часа — предложение истекает, место предлагается следующему | команда «Принять предложение»; новые события МестоПредложено (SeatOffered) и ПредложениеИстекло (SeatOfferExpired) |
Заодно решился вопрос из того же урока, нужны ли события для действий «предложить место» и «вернуть деньги». Нужны: на «Место предложено» реагируют Уведомления (письмо студенту) и таймер на 24 часа, на «Платёж возвращён» — Уведомления и поддержка. Ещё одно новое событие — «Счёт выставлен» (InvoiceIssued): его ждёт письмо со ссылкой на оплату.
Не всё закрывается сразу. Две горячие точки остались открытыми и ушли в канву Набора: «Поток перенесли на другую дату — что с записями и правилом 7 суток?» и «Возможен ли частичный возврат?». Это нормальный итог: горячая точка без ответа — не провал сессии, а честно записанное незнание.
Ещё одна находка: одна отмена или две
Заголовок раздела «Ещё одна находка: одна отмена или две»У стены разгорелся спор: «Студент отменил запись» и «Неоплаченная запись отменена по таймеру» — одно событие или два? Менеджер набора сказала, что для места разницы нет: оно освобождается в обоих случаях. Бухгалтер возразила: в первом случае может быть положен возврат по правилу 7 суток, во втором денег не было вовсе. Решили оставить одно событие ЗаписьОтменена, но с причиной отмены — студент или истёк срок оплаты, — а правило возврата применять только к первой причине. Такие споры о словах — та самая работа над единым языком из урока DDD.1.2, только у стены она идёт в разы быстрее.
Design Level: от стены к коду
Заголовок раздела «Design Level: от стены к коду»На Design Level команда приближает один участок — всё, что крутится вокруг большого жёлтого стикера «Поток». На нём записывают правила, которые он защищает, а вокруг собирают его команды и события. Получается почти готовый эскиз агрегата:
| Стикер на стене | Кандидат в коде (Набор) | Событие |
|---|---|---|
| команда «Записаться на поток» | cohort.enroll(student_id, now) | СтудентЗаписан (StudentEnrolled) |
| политика «Когда платёж получен — подтвердить запись» → команда | cohort.confirm(enrollment_id, now) | ЗаписьПодтверждена (EnrollmentConfirmed) |
| команда «Отменить запись» (студент или таймер 48 часов) | cohort.cancel(enrollment_id, now, reason) | ЗаписьОтменена (EnrollmentCancelled), МестоОсвободилось (SeatReleased) |
| политика «Когда место освободилось — предложить место» → команда | cohort.offer(now) | МестоПредложено (SeatOffered) |
| команда «Принять предложение» | cohort.accept_offer(student_id, now) | СтудентЗаписан (StudentEnrolled) |
| правила на жёлтом стикере | инварианты агрегата | места, «не дважды», закрытие при старте, 48 и 24 часа |
Имена в среднем столбце — черновик, а не окончательный дизайн: как сделать агрегат, который нельзя привести в неверное состояние, разберём в уроке DDD.3.2. Но направление уже видно: слова стены становятся именами в коде, события — классами событий, правила — проверками, которые тесты будут ловить по одному. Остальные стикеры тоже находят своё место:
| Стикер | Во что превращается | Где в программе |
|---|---|---|
| событие | класс доменного события | урок DDD.3.3 |
| команда | метод агрегата или сервиса приложения | уроки DDD.3.2 и DDD.3.3 |
| агрегат (ограничение) | агрегат с инвариантами | урок DDD.3.2 |
| политика | обработчик события, менеджер процесса | урок DDD.4.2 |
| модель чтения | запрос или проекция | урок DDD.4.1 |
| внешняя система | адаптер и предохранительный слой | урок DDD.2.2 |
| горячая точка | открытый вопрос в глоссарии и канве; позже — тест на принятое решение | уроки DDD.1.2 и DDD.2.1 |
Как провести сессию
Заголовок раздела «Как провести сессию»Подготовка
Заголовок раздела «Подготовка»- Кого звать. По шпаргалке DDD Crew — всех, кто приносит знания, и всех, кому эти знания нужны: экспертов и инженеров. Процесс DDD Crew добавляет людей продукта, тех, кто понимает нужды клиентов, и по возможности настоящих пользователей. Без экспертов получится Event Storming базы данных, а не бизнеса.
- Приглашение. Напишите цель встречи и в двух фразах — что такое Event Storming. Автор шпаргалки рассылает участникам заранее доклад Брандолини «50,000 Orange Stickies Later».
- Материалы. Рулон бумаги, много оранжевых стикеров и понемногу остальных цветов, одинаковые толстые маркеры (тонкую ручку не прочитать с двух метров), скотч. Материалов должно хватить с запасом: ничто так не сбивает сессию, как кончившиеся стикеры.
- Комната. Поверхность для моделирования около 6–8 метров, стол для материалов, легенда на виду и никаких стульев у стены: люди, которые стоят, двигаются и спорят, а сидящие смотрят в телефоны. Открывающиеся окна и немного еды — не мелочь, а условие трёхчасовой работы.
- Фасилитатор. Отдельный человек, нейтральный: он не продвигает своё решение, а помогает группе. Приходит первым и уходит последним.
Начало и конец
Заголовок раздела «Начало и конец»Шпаргалка советует начинать с check-in: каждый коротко говорит, как он и чего ждёт от встречи, — не о работе. Затем договорённости, записанные на флипчарте. Автор шпаргалки берёт три из практики Deep Democracy: каждый прав, ни у кого нет монополии на истину; мы начинаем разговор, чтобы углубить отношения; мы готовы учиться вместе. Потом — короткое введение: что такое доменное событие, легенда, пример. Заканчивают check-out: участники по кругу говорят, что вынесли из встречи. Сразу после сессии стену фотографируют по участкам, а в течение пары дней переносят в цифровой вид — иначе через неделю от неё останется рулон, который никто не развернёт.
Event Storming хорошо переносится на виртуальные доски — Miro и аналоги; есть и открытый инструмент DDD Toolbox с готовым шаблоном. Онлайн свои правила (это практические советы, а не догма):
- Подготовьте доску заранее: длинная горизонтальная область, палитра стикеров нужных цветов, легенда, отдельная «парковка» для горячих точек. Каждый участник должен до встречи один раз зайти на доску и создать стикер.
- Короче и чаще. Три часа у экрана тяжелее трёх часов у стены. Лучше две-три сессии по полтора-два часа с перерывами.
- Хаотичное исследование — по таймеру и молча, с включёнными камерами. Онлайн легче отвлечься, и фасилитатору труднее заметить, кто выпал.
- Помощник фасилитатора следит за доской: выравнивает стикеры, ловит дубликаты, отвечает на вопросы по инструменту.
- Плюс онлайна — стена уже цифровая, её не нужно переносить. Минус — меньше энергии и спонтанных разговоров у стены; их приходится провоцировать вопросами.
Типичные ошибки фасилитатора
Заголовок раздела «Типичные ошибки фасилитатора»| Ошибка | Что получается | Как правильно |
|---|---|---|
| позволить спору идти сорок минут | половина участников выключается, стена стоит | поставить горячую точку и двигаться дальше |
| начать Big Picture с команд и агрегатов | эксперты теряются в чужих терминах, а разработчики рисуют схему базы | сначала только события; остальные стикеры — когда понадобятся |
| позволить говорить во время хаотичного исследования | первый уверенный голос задаёт всю картину | 15–20 минут тишины, каждый пишет сам |
| самому писать стикеры или занимать сторону в споре | группа ждёт решения от фасилитатора | фасилитатор нейтрален, стикеры пишут участники |
| звать только разработчиков | стена о таблицах и экранах, а не о бизнесе | без экспертов сессию не проводить |
| пропускать стикеры «Оплата», «Сохранить в БД» | на стене команды, существительные и техника вместо событий | вежливо просить переписать: что случилось, в прошедшем времени, словами бизнеса |
| рано наводить порядок | дубликаты убраны без обсуждения, хотя за одним словом были разные понятия | сначала обсудить каждый дубликат, структуру добавлять постепенно |
| хотеть всё за одну встречу | сессия на восемь часов, к вечеру никто ничего не решает | Big Picture отдельно, процессы — отдельными сессиями |
| забыть про итоги | через неделю никто не помнит решений | сфотографировать, оцифровать, внести решения в глоссарий и канву |
Практика
Заголовок раздела «Практика»Стена в коде
Заголовок раздела «Стена в коде»После сессии стену оцифровывают. Удобно хранить процесс как строки «грамматики» — так его можно и прочитать, и проверить программой. Скрипт ниже делает три вещи, которые полезно делать после каждой сессии: проверяет черновые стикеры, собирает команды вокруг тех, кто принимает решение, — кандидатов в агрегаты, — и ищет дыры: политики без события и события, на которые никто не реагирует.
# Стена Process Level «записаться на поток и оплатить», перенесённая в код после сессии.# Строка = кто запускает (актор, таймер или политика «Когда …») → команда → кто решает → события.FLOW = [ ("Студент", "Записаться на поток", "Поток", ["Студент записан"]), ("Когда студент записан", "Выставить счёт", "Счёт", ["Счёт выставлен"]), ("Студент", "Оплатить счёт", "Платёжная система", ["Платёж получен"]), ("Когда платёж получен", "Подтвердить запись", "Поток", ["Запись подтверждена"]), ("Когда запись подтверждена", "Открыть доступ к занятиям", "Обучение", ["Доступ к занятиям открыт"]), ("Через 48 часов без платежа", "Отменить неоплаченную запись", "Поток", ["Запись отменена", "Место освободилось"]), ("Когда место освободилось", "Предложить место", "Поток", ["Место предложено"]), ("Студент из листа ожидания", "Принять предложение", "Поток", ["Студент записан"]), ("Через 24 часа без ответа", "Отозвать предложение", "Поток", ["Предложение истекло", "Место освободилось"]), ("Когда платёж получен, а запись отменена", "Вернуть платёж", "Счёт", ["Платёж возвращён"]),]OWNER = {"Поток": "Набор", "Счёт": "Оплата", "Платёжная система": "внешняя система", "Обучение": "другой контекст"}
# Черновые стикеры из хаотичного исследования — до чисткиDRAFT = ["Студент записан", "Оплата", "Запись сохранена в таблицу orders", "Отменить запись", "Место освободилось"]PAST_ENDINGS = ("ан", "ен", "ён", "на", "но", "ло", "лось", "лся", "лась", "ыт") # грубая эвристикаTECH_WORDS = ("таблиц", "orders", "бд", "кнопк", "api")
def check_sticker(text): """Что не так с оранжевым стикером: событие — факт предметной области в прошедшем времени.""" if any(word in text.lower() for word in TECH_WORDS): return "технический факт, а не событие предметной области" if not text.split()[-1].endswith(PAST_ENDINGS): return "не прошедшее время: это команда, процесс или существительное" return None
print("Черновые стикеры:")for text in DRAFT: print(f" {text:35} {check_sticker(text) or 'ок'}")
print("\nПроцесс по строкам грамматики:")for who, command, decider, produced in FLOW: print(f" {who:41} → {command:30} → {decider:17} → {', '.join(produced)}")
print("\nКандидаты в агрегаты и их контексты:")for decider in dict.fromkeys(d for _, _, d, _ in FLOW): commands = [c for _, c, d, _ in FLOW if d == decider] print(f" {decider} ({OWNER[decider]}): {'; '.join(commands)}")
events = {e for *_, produced in FLOW for e in produced}triggers = {who.removeprefix("Когда ").split(",")[0].capitalize() for who, *_ in FLOW if who.startswith("Когда ")}print("\nПолитика ждёт события, которого нет на стене:", sorted(triggers - events) or "нет")print("На событие никто не реагирует:", ", ".join(sorted(events - triggers)))Черновые стикеры: Студент записан ок Оплата не прошедшее время: это команда, процесс или существительное Запись сохранена в таблицу orders технический факт, а не событие предметной области Отменить запись не прошедшее время: это команда, процесс или существительное Место освободилось ок
Процесс по строкам грамматики: Студент → Записаться на поток → Поток → Студент записан Когда студент записан → Выставить счёт → Счёт → Счёт выставлен Студент → Оплатить счёт → Платёжная система → Платёж получен Когда платёж получен → Подтвердить запись → Поток → Запись подтверждена Когда запись подтверждена → Открыть доступ к занятиям → Обучение → Доступ к занятиям открыт Через 48 часов без платежа → Отменить неоплаченную запись → Поток → Запись отменена, Место освободилось Когда место освободилось → Предложить место → Поток → Место предложено Студент из листа ожидания → Принять предложение → Поток → Студент записан Через 24 часа без ответа → Отозвать предложение → Поток → Предложение истекло, Место освободилось Когда платёж получен, а запись отменена → Вернуть платёж → Счёт → Платёж возвращён
Кандидаты в агрегаты и их контексты: Поток (Набор): Записаться на поток; Подтвердить запись; Отменить неоплаченную запись; Предложить место; Принять предложение; Отозвать предложение Счёт (Оплата): Выставить счёт; Вернуть платёж Платёжная система (внешняя система): Оплатить счёт Обучение (другой контекст): Открыть доступ к занятиям
Политика ждёт события, которого нет на стене: нетНа событие никто не реагирует: Доступ к занятиям открыт, Запись отменена, Место предложено, Платёж возвращён, Предложение истекло, Счёт выставленКак читать результат:
- Черновые стикеры. «Оплата» — то самое двусмысленное слово, которого нет в глоссарии; у стены его превращают в «Счёт выставлен» или «Платёж получен». «Запись сохранена в таблицу orders» — факт о базе данных, а не о бизнесе. «Отменить запись» — команда, ей место на синем стикере. Проверка окончаний — грубая эвристика, а не разбор русского языка: она ловит частые ошибки, но последнее слово всегда за людьми.
- Кандидаты в агрегаты. Вокруг «Потока» собрались шесть команд и все правила Набора — это кандидат в главный агрегат школы. «Счёт» — агрегат Оплаты. Платёжная система — внешняя, с ней работают через предохранительный слой из прошлого урока.
- События без реакции. Три из шести — законные концы веток: «Доступ к занятиям открыт», «Платёж возвращён», «Предложение истекло» (место после него освобождается отдельным событием). А вот «Счёт выставлен» и «Место предложено» — сигналы: студенту никто не сообщает ни о счёте, ни о предложенном месте. На стене не хватает Уведомлений — нужны политики «Когда счёт выставлен — отправить ссылку на оплату» и «Когда место предложено — написать студенту». «Запись отменена» тоже требует вопроса: если отменил студент за 7 суток и более, кто запускает возврат? Это готовые вопросы к следующей сессии.
Попробуйте дописать в FLOW строки для Уведомлений и возврата при отмене студентом и запустите скрипт снова — список
событий без реакции должен сократиться до законных концов.
Упражнение: мини-сессия «отмена записи студентом» (30–40 минут)
Заголовок раздела «Упражнение: мини-сессия «отмена записи студентом» (30–40 минут)»Проведите Event Storming уровня Process Level для процесса «студент отменяет запись» — с коллегой или в одиночку, проговаривая вслух. Если есть возможность, позовите человека, который работал в поддержке любого сервиса: он знает, как на самом деле отменяют.
- Выложите события, начиная с «Запись отменена», и все её последствия. Только оранжевые стикеры, 10 минут.
- Добавьте команды, акторов, политики и внешние системы.
- Отметьте горячие точки. Подсказка: правило возврата зависит от времени до старта; запись может быть в разных статусах; освободившееся место кому-то предлагают.
- Сверьтесь с разбором.
Возможный разбор
События: «Запись отменена» (причина — студент), «Место освободилось», «Возврат назначен» или сразу команда «Вернуть платёж» в Оплату, «Платёж возвращён», «Место предложено». Политики: «Когда запись отменена студентом за 7 суток и более до старта — вернуть платёж»; «Когда место освободилось — предложить первому в листе ожидания»; «Когда платёж возвращён — написать студенту». Внешние системы: платёжная система, сервис рассылок.
Горячие точки, которые обычно всплывают: отмена записи в статусе «ожидает оплаты» — возвращать нечего, но место освобождается; отмена ровно за 7 суток — решено в уроке DDD.1.2: полный возврат (сравнение «больше или равно»); что, если возврат в платёжной системе не прошёл; можно ли отменить запись после старта потока. Последний вопрос — новый открытый вопрос для экспертов: запишите его в канву Набора.
Проверь себя
Заголовок раздела «Проверь себя»- Event Storming Альберто Брандолини — совместное моделирование на «бесконечной стене»: доменные события в прошедшем времени слева направо, а вокруг них команды, акторы, политики, системы, модели чтения, агрегаты и горячие точки. Порядок по времени вскрывает пробелы, которые прячутся в тексте требований.
- Три формата: Big Picture — весь бизнес и возникающие контексты; Process Level — один процесс полной грамматикой; Design Level — агрегаты, их правила и события внутри контекста.
- Сессию готовят: нужные люди (эксперты обязательно), материалы, длинная стена без стульев, нейтральный фасилитатор, договорённости. Онлайн — подготовленная доска, короткие сессии и помощник. Главная ошибка фасилитатора — давать спорам съедать время вместо горячих точек.
- На сессии «записаться на поток и оплатить» закрыты три открытых вопроса глоссария: повторная запись после отмены возможна; платёж после автоматической отмены возвращается; студент из листа ожидания подтверждает согласие занять место и получает запись «ожидает оплаты». Появились события «Счёт выставлен», «Место предложено», «Предложение истекло», «Платёж возвращён».
- Стена переходит в архитектуру: ключевые события, дорожки и смена языка дают границы контекстов, а команды и правила вокруг жёлтого стикера «Поток» — кандидата в агрегат, с которого начнётся тактическое проектирование.
Дальше: урок DDD.3.1 — сущности и объекты-значения. Начинаем
модуль тактического проектирования: превратим Money и Period из стикеров и глоссария в неизменяемые объекты с
правилами и узнаем, чем запись на поток отличается от суммы в копейках.