обновлением базы данных. Удачным решением является применение шаблона «Публикация событий»: сообщение в самом начале записывается в базу данных в рамках транзакции. Затем отдельный процесс извлекает сообщение из базы данных, используя шаблон «Опрашивающий издатель» или «Отслеживание транзакционного журнала», и передает его брокеру
Микросервисная архитектура является распределенной, поэтому межпроцессное взаимодействие играет в ней ключевую роль.
• К развитию API сервиса необходимо подходить тщательно и осторожно. Легче всего вносить обратно совместимые изменения, поскольку они не влияют на работу клиентов. При внесении ломающих изменений в API сервиса обычно приходится поддерживать как старую, так и новую версию, пока клиенты не обновятся.
• Существует множество технологий IPC, каждая со своими достоинствами и недостатками. Ключевое решение на стадии проектирования — выбор между синхронным удаленным вызовом процедур и асинхронными сообщениями. Самыми простыми в использовании являются синхронные протоколы вроде REST, основанные на вызове удаленных процедур. Но в идеале, чтобы повысить уровень доступности, сервисы должны взаимодействовать с помощью асинхронного обмена сообщениями.
• Чтобы предотвратить лавинообразное накопление сбоев в системе, клиент, использующий синхронный протокол, должен быть способен справиться с частичными отказами — тем, что вызываемый сервис либо недоступен, либо проявляет высокую латентность. В частности, при выполнении запросов следует отсчитывать время ожидания, ограничивать количество просроченных запросов и применять шаблон «Предохранитель», чтобы избежать обращений к неисправному сервису.
• Архитектура, использующая синхронные протоколы, должна содержать механизм обнаружения, чтобы клиенты могли определить сетевое местонахождение экземпляров сервиса. Проще всего остановиться на механизме обнаружения, который предоставляет платформа развертывания: на шаблонах «Обнаружение на стороне сервера» и «Сторонняя регистрация». Альтернативный подход — реализация обнаружения сервисов на уровне приложения: шаблоны «Обнаружение на стороне клиента» и «Саморегистрация». Этот способ требует бо'льших усилий, но подходит для ситуаций, когда сервисы выполняются на нескольких платформах развертывания.
• Модель сообщений и каналов инкапсулирует детали реализации системы обмена сообщениями и становится хорошим выбором при проектировании архитектуры этого вида. Позже вы сможете привязать свою архитектуру к конкретной инфраструктуре обмена сообщениями, в которой обычно используется брокер.
• Ключевая трудность при обмене сообщениями связана с их публикацией
Debezium (debezium.io) — проект с открытым исходным кодом, который публикует изменения базы данных для брокера сообщений Apache Kafka.
• LinkedIn Databus (github.com/linkedin/databus) — проект с открытым исходным кодом, который анализирует журнал транзакций Oracle и публикует изменения в виде событий. Компания LinkedIn использует Databus для синхронизации различных производных источников данных с системой записей.
Порождение событий не панацея. Вот его недостатки.
• Оно имеет другую модель программирования с высоким порогом вхождения.
• Оно так же сложно, как приложение, основанное на обмене сообщениями.
• Меняющиеся события могут создать проблемы.
• Усложняется удаление данных.
• Обращение к хранилищу событий связано с определенными трудностями.
Чтобы этого избежать, можно сделать так, чтобы событие публиковалось всегда. Если агрегат ничего не генерирует, приложение сохраняет псевдособытие лишь для того, чтобы записать ID сообщения. Такие псевдособытия потребитель должен игнорировать.
Если приложение задействует хранилище событий на основе СУРБД, оно может применить идентичный подход к обнаружению и отклонению дубликатов. ID сообщения вставляется в таблицу PROCESSED_MESSAGES в рамках транзакции, которая добавляет события в таблицу EVENTS.
В событийном фреймворке Eventuate Client, который мы подробнее рассмотрим в подразделе 6.2.2, эти методы называются process() и apply(). Первый принимает в качестве параметра командный объект, который содержит запрос на обновление, и возвращает список событий. Второй принимает событие и возвращает пустое значение. Агрегат определяет несколько перегруженных версий этих методов: по одной разновидности process() для каждого класса команд и по одному методу apply() для каждого типа событий, который генерируется агрегатом.
Событие должно содержать данные, необходимые агрегату для перехода к новому состоянию. Состояние агрегата включает в себя значения полей его объекта.
applyEvent(). Если вы знакомы с функциональным программированием, то, наверное, догадались, что это операция свертывания.
Но у него есть несколько недостатков и ограничений.
• Объектно-реляционный разрыв.
• Отсутствие истории агрегатов.
• Реализация журналирования для аудита требует много усилий и чревата ошибками.
• Публикация событий является лишь дополнением к бизнес-логике.