Антипаттерны Событийно-Ориентированной Архитектуры

И снова здравствуйте! В ожидании начала курса «Архитектор программного обеспечения» Мы подготовили перевод еще одного интересного материала.



Антипаттерны событийно-ориентированной архитектуры

В последние несколько лет наблюдается рост популярности микросервисной архитектуры.

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

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

Вы можете найти много литературы, которая расскажет вам, почему (возможно) микросервисы не будут лучшим выбором в вашем случае.

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

При правильном использовании микросервисы дали нам несколько преимуществ, а именно:

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

Микросервисы подразумевают модульность кода и всей инфраструктуры (базы данных и т. д.).

В правильно реализованной микросервисной архитектуре каждый сервис имеет собственную инфраструктуру.

Доступ к базе данных пользователей (чтение и запись) может осуществлять только сервис «Пользователи».

Организационная масштабируемость : Микросервисы помогают решать организационные проблемы и дают нам возможность управлять базой кода, которая модифицируется несколькими командами.

Разделение базы кода предотвращает конфликты при внесении изменений.

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

Кроме того, организация работы в небольших коллективах способствует развитию ответственности.



Событийно-ориентированная архитектура

Не все микросервисные архитектуры управляются событиями.

Некоторые люди выступают за синхронную связь между сервисами в такой архитектуре с использованием HTTP (gRPC, REST и т. д.).

В letgo мы стараемся не следовать этому шаблону и асинхронно связываем наши сервисы с события домена .

Вот причины, по которым мы это делаем: Улучшенная масштабируемость и стабильность.

: Разделение большой системы на более мелкие подсистемы позволяет контролировать последствия сбоев.

Например, DDoS или всплеск трафика на одном из сервисов не повлияет на остальные.

Если ваши сервисы работают синхронно, то при DDoS на одном сервисе пострадает и другой.

В данном случае можно сказать, что услуги слишком взаимосвязаны.

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



Антипаттерны событийно-ориентированной архитектуры

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

.

Отключение : Изменение одного сервиса не должно влиять на другие.

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

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

Сущность пользователя в нашей службе «Пользователи» не обязательно должна быть той же сущностью, что и в службе чата.

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

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

Разделение кода без преобразования коммуникационной инфраструктуры в асинхронную превратит ваше приложение в распределенный монолит.

Антипаттерны событийно-ориентированной архитектуры



Событийно-ориентированная архитектура в Letgo

Сегодня я хочу поделиться примером того, как мы в letgo используем события предметной области и асинхронную связь: наша сущность User существует во многих сервисах, но ее созданием и редактированием изначально занимается сервис Users. В базе данных службы «Пользователи» мы храним много данных, таких как имя, адрес электронной почты, аватар, страна и т. д. В нашей службе чата у нас также есть концепция пользователя, но нам не нужны данные, которые сущность «Пользователь» из Служба пользователей имеет. В списке диалогов отображается имя пользователя, аватар и ID (ссылка на профиль).

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

В чате мы на самом деле не говорим о пользователях, мы называем их «говорящими».

Эта проекция относится к службе чата и строится на основе событий, которые чат получает от службы пользователей.

То же самое мы делаем с листингами.

В сервисе «Товары» мы храним n изображений каждого листинга, но в представлении списка бесед мы показываем одно основное, поэтому для нашей проекции из «Товаров в чат» требуется только одно изображение вместо n.

Антипаттерны событийно-ориентированной архитектуры

Посмотреть список разговоров в нашем чате.

Он показывает, какая конкретная серверная служба предоставляет информацию.

Если вы еще раз посмотрите на список разговоров, то увидите, что почти все показываемые нами данные не созданы службой Чата, а принадлежат ему, поскольку представления «Пользователь» и «Чат» принадлежат Чату.

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



Антипаттерны событийно-ориентированной архитектуры

Упрощенный взгляд на архитектуру letgo


Антипаттерны

Некоторые интуитивные решения часто становились ошибками.

Вот список наиболее важных антипаттернов, с которыми мы столкнулись в нашей архитектуре событий предметной области.

1. Плотные события Мы стараемся сделать события нашей предметной области как можно меньшими, не теряя при этом их смысловой смысл.

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

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

В этом случае лучше рассмотреть рефакторинг со стороны.

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

Нам также необходимо быть осторожными с проблемой «продукт-пользователь»: многие системы используют сущности «продукт» и «пользователь», и эти сущности, как правило, несут в себе всю логику, а это означает, что все события предметной области связаны с ними.

2. События как намерения Событие предметной области по определению — это событие, которое уже произошло.

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

Обычно мы говорим о событиях в предметной области в прошедшем времени: ser_registered , продукт_опубликовано и т. д. Чем меньше один сервис знает о других, тем лучше.

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

3. Никакой независимой сериализации или сжатия.

Системы сериализации и сжатия событий в нашей предметной области не должны зависеть от языка программирования.

Вам даже не нужно знать, на каком языке написаны потребляющие сервисы.

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

В letgo мы используем JSON, но существует множество других форматов сериализации с хорошей производительностью.

4. Отсутствие стандартной структуры.

Когда мы начали переводить серверную часть letgo на событийно-ориентированную архитектуру, мы договорились об общей структуре событий предметной области.

Это выглядит примерно так:

  
   

{ “data”: { “id”: [uuid], // event id. “type”: “user_registered”, “attributes”: { “id”: [uuid], // aggregate/entity id, in this case user_id “user_name”: “John Doe”, … } }, “meta” : { “created_at”: timestamp, // when was the event created? “host”: “users-service” // where was the event created? … } }

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

5. Отсутствие проверки схемы.

Во время сериализации мы в letgo столкнулись с проблемами с языками программирования без строгой типизации.



{ “null_value_one”: null, // thank god “null_value_two”: “null”, “null_value_three”: “”, }

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

В letgo мы переходим на Avro и реестр Confluent Schema, который предоставляет нам единую точку определения структуры событий для нашего домена и позволит нам избежать подобных ошибок, а также устаревшей документации.

6. Анемичные события в домене Как я уже говорил ранее, как следует из названия, события предметной области должны иметь значение на уровне предметной области.

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

Проиллюстрируем это следующим примером: товар в нашей системе имеет геолокацию с широтой и долготой, которые хранятся в двух разных полях таблицы товаров сервиса «Товары».

Все продукты можно «переместить», поэтому у нас будут события домена, отражающие это обновление.

Раньше для этого у нас было два мероприятия: Product_latitude_updated И Product_longitude_updated , что не имело особого смысла, если только вы не были ладьей на шахматной доске.

В этом случае события будут иметь больше смысла.

Product_location_updated или Product_moved .



Антипаттерны событийно-ориентированной архитектуры

Ладья – шахматная фигура.

Ранее назывался тур.

Ладья может перемещаться только по вертикали или горизонтали через любое количество незанятых полей.

7. Отсутствие инструментов отладки.

В Letgo мы производим тысячи доменных событий в минуту.

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

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

Запросы типа «покажи мне все события, созданные пользователем John Doe за последние 3 часа», также могут быть полезны для обнаружения мошенничества.

Для этих целей мы разработали несколько инструментов на ElasticSearch, Kibana и S3. 8. Отсутствие мониторинга событий Мы можем использовать события домена для проверки работоспособности системы.

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

Например, если мы развернем новую версию сервиса «Продукты» в производстве и увидим уменьшение количества событий продукт_опубликовано на 20%, можно с уверенностью сказать, что мы что-то сломали.

В настоящее время мы используем InfluxDB, Grafana и Prometheus для достижения этой цели с помощью производных функций.

Если вы помните курс математики, то поймете, что производная функции f(x) в точке x равна тангенсу касательного угла, проведенного к графику функции в этой точке.

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

С помощью этих уведомлений вы можете не говорить «сообщите мне, если мы будем публиковать менее 200 событий в секунду в течение 5 минут» и сосредоточиться на существенном изменении скорости публикации.



Антипаттерны событийно-ориентированной архитектуры

Здесь произошло что-то странное.

А может, это просто маркетинговая кампания.

9. Надеюсь, что все будет хорошо Мы стараемся создавать устойчивые системы и снижать затраты на их восстановление.

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

Нам нужен план, с помощью которого мы сможем восстановить правильное состояние системы, переобработав все потерянные события.

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

В letgo это скорее работа команды данных, чем команды бэкенда.

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

Это может быть необходимо, если вам необходимо восстановиться после сбоя и повторно обработать события или потому, что шина сообщений доставляет сообщение больше чем единожды .

Идемпотентность – это, на наш взгляд, самое дешевое решение данной проблемы.

Представьте, что мы слушаем событие в нашем сервисе.

user_registered из службы Пользователи , потому что нам нужна проекция пользователя, а также у нас есть таблица MySQL, которая использует ID пользователя в качестве первичного ключа.

Если при обработке событий предметной области user_registered , мы не будем проверять наличие перед вставкой, мы можем столкнуться с дополнительными ошибками дублирования ключей.

В таком случае, даже если мы проверим существование пользователя перед вставкой, мы все равно можем получить ошибку из-за задержки между главным и подчиненным MySQL (в среднем около 30 мс).

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

В зависимости от того, насколько критичным является ваш вариант использования на уровне домена, вам следует решить, насколько вы терпимы к сбоям и несоответствиям и стоит ли система дедупликации затрат. 10. Отсутствие документации по предметным событиям.

События нашей предметной области стали нашим общедоступным интерфейсом для всех серверных систем.

Точно так же, как мы документируем наши REST API, нам также необходимо документировать события предметной области.

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

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

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

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

До встречи на курсе! Теги: #Микросервисы #ddd #микросервисы #События домена

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.