Три мушкетёра — Event Sourcing, Event Storming и Event Store — вступают в бой: Часть 1 — пробуем Event Store ДБ
Привет, Хабр! Решил я значит на время отойти от Scala, Idris и прочего ФП и чуть чуть поговорить о Event Store — базе данных в которой можно сохранят события в потоки событий. Как в старой доброй книге у нас тоже мушкетёров на самом деле 4 и четвертый это DDD. Сначала я с помощью Event Storming выделю команды, события и сущности с ними связанные. Потом сделаю на их основе сохранение состояния объекта и его восстановление. Буду я делать в этой статье обычный TodoList. За подробностями добро пожаловать под кат.
Содержание
Ссылки
Собственно говоря Event Store это БД которая предназначена для хранения событий. Так же она умеет создавать подписки на события чтобы их можно было как-то обрабатывать. Так же там есть проекции которые так же реагируют на события и на их основе аккумулирую какие-то данные. Например можно при событии TodoCreated увеличивать какой-то счетчик Count в проекции. Пока что в этой части я буду использовать Event Store как Read и Write Db. Дальше в следующих статьях я создам отдельную БД для чтения в которую будут данные записываться на основе событий сохраненных в БД для записи тобишь Event Store. Так же будет пример того как делать «Путешествия во времени» откатывая систему к состоянию которая она имела в прошлом.
И так начнем Event Stroming. Обычно для его проведения собирают все заинтересованных людей и экспертов которые рассказывают какие события в той предметной области которую ПО будет моделировать. Например для ПО завода — ИзделиеИзготовлено. Для игры — ПолученУрон. Для Финансового ПО — ДенгиЗачисленыНаСчет и прочее в этом духе. Так как наша предметная область это максимально простой TodoList то событий у нас будет немного. И так, запишем на доске события нашей предметной области (домена).
Теперь добавим команды которые эти события вызывают.
Далее сгруппируем эти события и команды вокруг сущности с изменением состояния которой они связаны.
Команды у меня превратятся просто в названия методов сервиса. Приступим к реализации.
Сначала опишем События в коде.
Теперь наше ядро — сущность:
Подключаемся к Event Store:
И так, главная часть. Собственно сохранение и чтение событий из Event Store:
Хранилище сущностей выглядит совсем просто. Мы достаем из EventStore события сущности и восстанавливаем ее из них или просто сохраняем события сущности.
Сервис в котором происходит работа с репозиторием и сущностью:
Ну собственно пока ничего впечатляющего. В следующих статься когда я добавлю отдельную БД для чтения все заиграет другими красками. Это сразу нам повесит консистентность со временем. Event Store и SQL БД по принципу мастер — слейв. Одна белая ES и много черных MS SQL из которых читают данные.
Лирическое отступление. В свете последних событий не мог не пошутить по поводу мастер слейв и черных белых. Эхе, уходит эпоха, будем внукам говорить что жили во времена когда базы при репликации называли мастер и слейв.
В системах где много чтения и мало записи данных (таких большинство) это даст прирост скорости работы. Собственно сама репликация мастер слейв она направленна на то что у вас замедляется запись (как и с индексами) но взамен ускоряется чтение за счет распределения нагрузки по нескольким БД.
Event Storming на практических кейсах
Выступил на митапе сообщества DDDevotion в Райфайзен Банке с докладом про Event Storming. Ниже краткий пересказ по итогам.
Введение
При появлении желания перейти на микросервисную архитектуру у людей появляется множество вопросов. Вот лишь некоторые из них:
И действительно, как правильно проектировать? Однозначного ответа нет и не будет. Если бы он появился, то не на долго, — сразу же появилась бы другая, стремящаяся занять пьедестал! Но есть хорошие практики, которые показали свою состоятельность на практике. Одна из такиз практик:
Какую проблему решаем?
При разработке сложных систем мы оказываемся в ситуации, при которой множество людей работают над проектом, который они в действительности не понимают. Каждый обладает какой-то частью знаний о проекте, но совместить эти знания в единую картинку бывает достаточно сложно. Нам нужно как-то этих людей собрать вместе, дать им инструмент извлечения знаний, причем инструмент простой и понятный и при этом действенный.
Из сложности понимания мы приходим к ситуации, когда требования не полны, противоречивы или попросту неверны. Некоторые пытаются исправить ситуацию с помощью Кафки 🙂 Но правда в том, что ни одна технология не поможет исправить плохих или неверных требований. Совсем плохо, если начинается поиск виноватых. Но чаще всего — никто не виноват. Скорее, нужна техника, которая поможет построить достаточно полную общую модель на основе разрозненных знаний всех вовлеченных в разработку и использование продукта людей.
Основная задача Event Storming: «Формирование общего понимания бизнес-процесса разрабатываемого продукта». Звучит обще и немного не инновационно, зато просто.
Event Storming помогает
И в целом Event Storming — великолепная практика, облегчающая дальнейшую работу в области Domain Driven Design, проектирования микросервисов и планирования событий в событийных архитектурах.
Что это такое?
Alberto Bandolini, автор метода, дал ему следующее определение:
Но остается вопрос, так зачем же его использовать, если есть традиционные методы. Не стану их здесь называть, чтобы не разжигать войн 🙂
Начнем с того, что Event Storming быстрее и приятнее, чем традиционные методы моделирования процессов. Нет рутины, всё в движении и в бурном, живом обсуждении.
Общий язык
По мере обсуждения формируется общий язык между IT и бизнесом. Важность этого сложно переоценить — это избавляет нас от ТЗ формата «Перевод с языка бизнеса на технический». Почему? Потому что код, дизайн, архитектура могут и должны использовать терминологию бизнеса, они как-бы отражают бизнес.
Здесь есть еще один важный момент — участники начинают улавливать тонкие различия между Сущностями, которые, если посмотреть только на данные, обозначают одно и то же. Так, если мы говорим о конференциях, то сущность Пользователь может уйти вовсе, но появятся сущности Посетитель, Спикер, Волонтер. И это разные сущности в разных контекстах, и даже атрибут «Компания» у каждой сущности может быть свой: выступаю от лица одной компании, пришел на конференцию от лица другой. Хотя на уровне модели данных атрибут может выглядит как один и тот же.
Итеративность
Другим важным аспектом является итеративность метода. Мы можем постепенно добавлять новые детали с каждой сессией, что снижает когнитивную нагрузку на участников во время каждой конкретной сессии.
Итеративность дает нам еще одно премущество: мы можем фокусно отбирать участников каждой сессии в зависимости от конкретных вопросов, требуемых обсуждения.
Связь с DDD
С точки же зрения моделирования Event Storming предоставляет высокоуровневую картинку решения, помещая детали технической реализации в контекст бизнес-процесса, как результат — становится очень эффективной техникой для перехода к DDD.
Event Storming уже содержит в своей структуре команды, события, слушателей (Listeners, как связку между внешними системами и Policy) и Агрегаты.
Как выглядит результат?
Ниже результат проведения первой итерации Event Storming на 40 человек в отдельном, достаточно большом, домене бизнеса:
После совместного выявления всех бизнес-событий, происходящих в системе и команд, инициирующих эти события, бизнес-люди на время оставили процесс (но аналитики остались) и мы перешли к выделению Агрегатов. Структура агрегатов показана на изображении ниже:
По факту каждый агрегат в части реализации — это либо отдельный микросервис, либо модуль. Главное здесь то, что мы построили слабосвязанную модель на уровне бизнес-сущностей. Если взять эту модель как стратегический дизайн, то реализация становится тактикой наполнения модели техническими деталями: кодом и данными. В чем смысл? Смысл в том, что соблюдая границы, определенные моделью, мы просто физически не сможем создать зависимости на уровне кода и данных. А как мы знаем, тормозят нас две вещи: зависимости и технический долг. Неплохой способ устранить хотя бы один из факторов торможения 🙂
Структура модели Event Storming
Ниже общая структура компонентов, из которых состоит модель Event Storming.
Здесь Command — это решение, принятое пользователем как ответ на некоторую информацию, полученную из Read Model и представленную посредством Screen Layout. Command всегда формулируется с глаголом в инфинитиве, например: «Открыть депозит», «Заказать такси». Read Model в таком случае может представлять собой данные со страницы депозитов или карту расположения таксистов.
Actor (фигурка человека) — это тот, кто инициирует команду. Ответить на Command и инициировать Event (событие) — ответственность Системы или Агрегата.
Policy или Procedures — это дополнительные бизнес-правила, которые могут самостоятельно инициировать команды. Например, при заказе такси, Policy может учитывать рейтинг водителя, а Procedure принимать решение о выборе алгоритма расчета стоимости поездки.
В конечном счете, Агрегат, относящиеся к нему команды, события и модель чтения вполне могут представлять микросервис (разумеется, это наше предположение и это далеко не единственный способ четко определить границы сервиса).
Как провести?
Вам понадобятся люди с вопросами и люди с ответами на эти вопросы.
Ценность именно во взаимодействии между людьми — пропустишь одного и пропустишь N-1 взаимодействий. Не стоит переживать, если какие-то вопросы останутся без ответов. Итеративная природа метода подразумевает возможность собраться повторно, предварительно проведя небольшое исследование проблемной области.
Место
Потребуется много свободного пространства.
Основная идея в том, что размер проблемы нельзя узнать без исследования, а значит — заранее неизвестно, сколько места потребуется. Столы лучше убрать — они создают неправильное физическое пространство: положения людей фиксированы, поза далека от оптимальной, слишком легко отвлечься на ноут или телефон.
Инструменты
Представьте себе много стикеров. Очень много стикеров. И возьмите в 10 раз больше. Лучше пусть останутся, чем прерывать сессию из-за такой мелочи. Помимо этого понадобится рулонная бумага (ее удобнее сворачивать и забирать с собой, чем листы от флипчарта) и маркер каждому участнику.
Проведение
Приведу классический, достаточно эффективный способ проведения, разбитый на итерации
Итерация #1
Цель: выявить максимальное количество событий.
Итерация #2
Цель: выявить команды, действующих лиц, внешние системы и модели чтения
Чтобы не мешать друг другу, можно договориться, кто с какой частью модели будет работать.
Итерация #3
Цель: выявить правила и процедуры. Они заполняют собой пустоты между Command и Event. Можно задавать вопросы вида «Это происходит всегда?» или «Это Событие всегда следует за этой Командой?».
На примере выше Refund Policy — это как раз правило возврата денежных средств. Конкретные детали выявим позже, сейчас для нас главное узнать о том, что такое правило существует.
Итерация #4
Цель: выявить агрегаты.
Первое правило выявления Агрегатов — не говорить слово Агрегат 🙂 Этот термин может запутить, но, конечно, все зависит от начальной подготовки участников.
Агрегат — это своего рода State Machine (конечный автомат) системы. Это то, что принимает команду и решает, реагировать на нее или нет. Можно не вводить термин агрегат, а ввести термин Система. Когда поток событий становится более четким, воспроизводим события еще раз. Если на карточке не существующая (то есть еще не разработанная) Система, можем дать ей какой-нибудь бессмысленное название. Какие-то команды будут относится к этой Системе. Когда проход завершен, вернитесь к именам. Они должны прийти сами собой.
Рекомендации
В конце хотелось бы дать ряд рекодментаций, повышающих эффективность сессий:
Спасибо DDDevotion и лично Жене Пешкову за приглашение и возможность выступить на митапе. Ниже полная презентация, а через какое-то время на хабра-канале Райфайзен Банка
Моделирование микросервисов с помощью Event storming
Event storming — метод, который смещает акцент у событий с технического на организационный и бизнес уровни и помогает создать устойчивую модульную систему. Он нередко используется в контексте моделирования микросервисов. Но как применить его на практике?
При создании системы на микросервисах можно легко получить распределенный монолит. Event Storming не уберегает от этого на 100%, но позволяет существенно снизить риск этого события. О том, как именно этого добиться, рассказал в своем докладе на конференции TechLead Conf 2020 практикующий консультант по архитектуре, процессам разработки и продуктовым практикам Сергей Баранов.
Как строить нашу микросервисную архитектуру быстрее, а команды делать более автономными при помощи Event storming?
Автор метода, итальянский программист Альберто Брандолини, дал свое определение.
Event storming — это увлекательный способ собрать вместе разработчиков и экспертов в бизнесе для быстрого, совместного исследования сложной предметной области бизнеса. Часы вместо дней и недель.
Ключевое слово здесь — «исследование», потому что вначале Event Storming выглядит как своего рода хаос, со временем обретающий четкую структуру.
Альберто Брандолини достаточно смело заявил, что подобное исследование может занять часы вместо дней и недель. И практика показывает, что это действительно так. На момент написания статьи, Сергей провел сессии Event Storming уже более 150 раз: для больших предметных областей, для небольших стартапов и для крупных энтерпрайзов. И каждый раз это работало достаточно эффективно.
Структура
Структура Event Storming достаточно проста и многое взяла из DDD:
Клиент инициирует выполнение команды. Команда всегда формулируется в будущем времени: это запрос на выполнение действия. Оно еще не выполнено и может быть отменено.
Допустим, речь идет о том, чтобы добавить товар в корзину. Команда приходит в агрегат (термин из DDD), либо уходит во внешнюю систему. Если команда может быть выполнена без нарушения инварианта агрегата, создается событие. Событие — это факт и действие, произошедшие в прошлом. В отличие от команды, событие не может быть отменено («Товар добавлен в корзину»).
На основе событий, например, обновляется модель чтения, цель которой — помочь пользователю в принятии решений («Купи еще на 1000 рублей и получи скидку 10%») и выполнении последующих команд. Или же событие может активировать некое бизнес-правило. Которое, в свою очередь, вызовет команду, и цикл повторится.
Альберто Брандолини называет эту схему «картинкой, которая объясняет все». По большому счету, так и есть: при повторении таких цепочек и получается наш конечный дизайн.
Процесс
На сессию приходят и технические и бизнес-специалисты. Вместе они размещают происходящие в бизнесе события в хронологическом порядке, попутно уточняя их смысл и избавляясь от предположений о происходящем в бизнесе.
Это абсолютно все события, происходящие в домене. Их могут быть сотни, и даже тысячи.
Товар добавлен в корзину;
Затем к этим событиям мы приписываем команды.
Все очень просто. Товар добавлен в корзину. Команда, которая относится к этому событию: «Добавить товар в корзину».
Постепенно идет усложнение и появляются определенные бизнес-правила.
Здесь очень хорошо помогают именно представители бизнеса, потому что программисты, по сути, переводят эти бизнес-правила в код. И важно здесь не то, как технически это выглядит, а то, как продукт работает в реальном бизнесе.
Появляются агрегаты. Агрегат — это сущность или кластер сущностей, которые мы рассматриваем как единое целое. Допустим, у сущности «Заказ» есть свои сущности: «Товары», «Адрес доставки» и пр. Наш интерес в том, что агрегат является транзакционной границей и отвечает за целостность и непротиворечивость всех данных, которые в него входят.
В нотации Event Storming агрегаты обозначаются желтыми стикерами. Вначале мы можем оставить эти стикеры пустыми, без названия. Мы пока не знаем, что это за агрегат. Просто будем собирать вокруг него те команды и события, которые, как нам кажется, должны обрабатываться вместе. А уже потом назовем его.
Агрегаты сбиваются вместе и образуют то, что в терминах DDD называется ограниченными контекстами. Они представляют из себя модель + общий словарь.
Ограниченные контексты должны быть максимально автономными. Такая автономность помогает в их рамках делать независимые друг от друга микросервисы. Хочу показать вам, как это выглядит на живом примере.
Стикеры на скрине мелкие. Но такая детализация выбрана не случайно: я хочу продемонстрировать объем происходящего.
Над этой схемой работали около пяти часов. Вначале выявили события, затем команды, добавили бизнес-правила. Со временем результатом командной работы стал сбор некоторых команд и событий в контексты. Например, контекст работы с ботами, контекст диалога с клиентом, контекст закрытия заявки.
В этом примере показана работа службы поддержки. Это промежуточный вариант. Но даже тут видно, что стратегия уже начинает вырисовываться.
В оффлайн это выглядит примерно так: перед нами огромная пустая стена, и около 40 человек генерируют:
События — оранжевые стикеры;
Команды — синие стикеры;
Агрегаты — большие желтые стикеры. В данном случае агрегаты являются нашими потенциальными микросервисами.
Некоторые микросервисы включают в себя несколько агрегатов. Преимущество в том, что все они могут разрабатываться автономно. Можно было сделать это и в Big Design Up Front, долго собирая предметную область с помощью архитектора или гвардии аналитиков. Но, собравшись все вместе, участники получили этот результат примерно за 8-9 часов.
Выглядит немного хаотично, но все великолепно оцифровывается, складывается в общую базу знаний, и у любого сотрудника компании есть доступ к этим данным.
Контексты
Посмотрим на контексты чуть ближе:
В примере выше речь идет о страховании. Разберем 4 контекста:
У них есть свои цели и своя ответственность. Например, цель экспертизы — контроль повышения качества оказания услуг, а ответственность — это поиск нарушений.
Проставим типы этих контекстов. В общем случае их три:
Core — основной контекст;
Supporting — поддерживающий контекст;
Generic — общий для всех.
Зачастую типы достаточно сложно определить. Люди в команде начинают о них спорить. Чтобы этого не произошло, можно использовать простую практику:
Выписываем контексты и спрашиваем, насколько «Экспертиза» обеспечивает наше конкурентное преимущество/насколько сложна ее реализация:
L — сильное влияние/сложная реализация;
M — среднее влияние/средняя сложность;
S — слабое влияние/низкая сложность.
Получаем набор значений. Нас интересует в первую очередь строчки с двумя L — то, что обеспечивает конкурентное преимущество, но может быть сложно реализовано. Кажется, что мы не можем отдать это на аутсорс или использовать внешнее ПО: это сердце нашего бизнеса.
Core туда, где есть, как минимум, (L,L), (L,M);
Supporting туда, где нет особого конкурентного преимущества. В данном случае это загрузка справочников;
Получаем такую картинку:
Казалось бы — зачем нам все это нужно?
Решения по разработке
По очень простой причине: мы можем принять ряд решений.
«Экспертиза» останется во внутренней разработке. Здесь будут сосредоточены основные микросервисы с основной бизнес-логикой.
Загрузку справочников есть смысл либо отдать на аутсорс, либо оставить для прокачки джуниоров.
Generic (поиск и авторизация) — это то, что можно использовать повторно. Здесь открываются новые варианты: купить решение, закрутить его в контейнер, и пусть оно там живет с небольшой кастомизацией.
Таким образом мы не распыляем силы, а видим, что у нас есть авторизация, экспертиза, поиск, загрузка справочников; понимаем, на что мы бросаем максимальное количество усилий и где будем прорабатывать предметную область дальше.
Стили коммуникации
Определяем, какими должны быть связи:
Это делается достаточно быстро. Здесь же мы обычно рассматриваем нефункциональные требования.
Например, для экспертизы они таковы: как часто экспертиза будет обращаться к авторизации, и какую пропускную способность должна авторизация держать.
У нас уже есть предметная область для экспертизы. И мы можем сказать, каким из этих элементов предметной области требуется постоянная авторизация доступа, или какие объемы данных будут передаваться из справочников. То есть появляются интеграционные связи. И на то, чтобы этого добиться, потребовалось меньше двух дней.
Протоколы взаимодействия
В качестве протоколов взаимодействия мы будем использовать Rest и RabbitMQ. Почему RabbitMQ? При загрузке справочников понадобится трансформация данных, не только чисто техническая, но и обеспечивающая защиту предметной области от протекания абстракций.
В DDD для защиты модели мы можем использовать шаблон ACL (nti-Corruption Layer). По сути, это дополнительный слой адаптеров между контекстами, позволяющий осуществлять взаимодействие в обоих направлениях и снимающий зависимость контекстов друг от друга. То есть он защищает вашу предметную область, чтобы туда не попадали специфичные для других контекстов данные.
Сами справочники могут храниться абсолютно в любом виде. Важно, чтобы в конкретных микросервисах мы работали со своей предметной областью, то есть брали из справочников только то, что нам нужно, и переводили эти данные в нашу предметную область. А еще важно придерживаться простого правила: именовать микросервисы и переменные в коде терминами, понятными бизнесу. Тогда нам не нужно будет переводить данные из одного языка в другой. Эти термины мы выявили еще на этапе поиска событий, команд и агрегатов.
На протоколы взаимодействия по-прежнему ушло меньше двух дней.
Детали реализации
Ограниченные контексты мы строим так, чтобы они были автономными. Внутри каждого из них мы можем использовать легковесные подходы, вроде C4 от Саймона Брауна, для того, чтобы спроектировать каждый из этих контекстов в отдельности.
Важно, что внутри каждого из контекстов команда самостоятельно может провести дальнейшую декомпозицию и проектирование. При том, что на ранних этапах мы уже определили основные нефункциональные требования.
У нас есть общая картинка, сформулированные архитектурные ограничения. В рамках таких ограничений команды могут самостоятельно принимать инженерные решения.
Команды закончили работу. Допустим, каждая решила использовать свою собственную базу данных:
Экспертизе подходит Postgres;
Загрузке справочников — MongoDB;
Для поиска будем использовать CockroachDB.
У нас появляется свобода выбора и возможность принимать решения внутри своей команды. Почему так? Потому, что за рамками контекста находится четко определенный интерфейс.
На детали реализации ушло не более трех дней. И то только потому, что нужно посмотреть, соответствуют ли базы данных нашим нефункциональным требованиям.
Сервисы должны быть построены вокруг бизнес-потребностей и развертываться независимо. Event Storming позволяет очень быстро определить бизнес-потребности и достаточно быстро, через практики DDD, перейти к техническому решению.
К чему мы идем?
При помощи связки Event Storming + Domain-Driven Design получилось быстро и эффективно получить независимые контексты, в рамках которых могут жить наши микросервисы. Вполне вероятно, что в будущем появятся новые актуальные практики. Но на текущий момент именно благодаря Event Storming есть возможность сделать это быстро и автономно.
Выводы
Event Storming помогает быстро получить стратегический дизайн, определив границы, в рамках которых технические решения можно принимать автономно;
Если преднамеренно не нарушать границы на уровне реализации, то зависимости не появятся;
А все мы помним, что в разработке нас тормозят две вещи: зависимости и технический долг. Даже если мы не понимаем, что разрабатывать, мы все равно можем написать это очень быстро:) Правда, не факт, что это будет именно то, что нужно клиенту. Когда же появляются зависимости, скорость разработки падает, ведь нам требуется координация.
За счет автономности значительно упрощается управление техническим долгом;
Появляется возможность эволюционного развития к целевому видению.
Event Storming — это не та сессия, которую можно провести, и забыть о ней. И бизнес, и архитектура развиваются. Поэтому приходится задействовать дополнительные сессии, дополнять события, проверять агрегаты.
Но повторные сессии занимают еще меньше времени. Ведь получается поддержать общее видение и общую ментальную модель. Наверное, это самое важное, что дает Event Storming.
Узнать еще больше подробностей о современных решениях для микросервисов, вы можете подписавшись на канал автора доклада, Сергея Баранова.
Хотите стать спикером TechLead Conf 2021? Если вам есть что сказать, вы хотите подискутировать или поделиться опытом — подавайте заявку на доклад.
Следите за нашими новостями в Telegram, Twitter, VK и Fb и присоединяйтесь к обсуждениям.














