Спасибо за комментарий.
Да, сагу можно развернуть в набор конструкций с try-catch, но это будет выглядеть довольно громоздко, когда необходимо сделать значительное количество шагов. Вот, например, развернутая сага из трех шагов:
По своей сути сага в нашей библиотеке содержит лишь довольно простой алгоритм для вызова действий в определенной последовательности, не более. Поэтому на обеспечение консистентности работают уже конкретные реализации этих действий.
Например, у нас есть проект, где в рамках бизнес-процесса задействуются 6 сервисов, в каждом сервисе используется модель хранения данных в виде событий, а значит, что для изменнения данных в рамках одного сервиса делается только insert в БД. В этом случае сага состоит из 6-ти основных шагов, где выполняются локальные доменные действия и делаются insert-ы событий во временные таблицы. Далее в этой же саге выполняется еще 6 шагов, где созданные записи переносятся в основные таблицы. В случае отката, например, если на шаге 4 не пройдет валидация данных, события из временных таблиц сервисов 1-3 будут удалены. По сути это некая реализация двухфазного коммита. В качестве транспорта используется RabbitMQ с настройками, гарантирующими доставку сообщения. Так что, консистентность здесь вполне достижима.
По поводу оберток и зависимостей. Да, мы это понимаем, поэтому у нас есть задачи в бэклоге, которые нацелены как раз на переход к нативным механизмам и абстракциям платформы (например, тот же ILogger), а также на более глубокую декомпозицию функционала.
Вместо сервисов — нет. Да и разве можно это полноценно реализовать через него? Он не для этого. Использовали только по прямому назначению — для отложенного запуска задач.
Наш фреймворк делался для запуска рестовых сервисов, а не для бэкграунд воркеров.
Совсем не обязательно ждать, когда что-то появится непосредственно в библиотеке. Можно сделать свою реализацию конфигуратора, которая использует абстракции корневого билдера, завернуть в свой пакет, и подключать во все необходимые сервисы.
В подобных конфигураторах можно сделать более сложную логику, добавить другие хэндлеры, считывать дополнительные параметры из файла конфигурации. Ну и затем, через свой метод расширения, подключить:
Это про то, что теперь вместо пяти разных сервисов, можно заинжектить какой-нибудь IMediator и юзать его?
— Да
Если да, то есть мнение, что это тот же service locator.
— Нет. Медиатор — это внутренняя шина, по которой можно отправлять сообщения или команды, на которые будут реагировать обработчики. Таким образом проще соблюдать SRP, ведь каждый обработчик отвечает только за обработку одной конкретной команды.
Да, пример, не выложили…
Это по сути для «пустого» сервиса. Полноценный пример будет позже в репозитории.
Program.cs:
class Program
{
public static void Main(string[] args)
{
CompanyHostBuilder.Create()
.UseServer((b, c) => { b.UseKestrel(); })
.BuildWebHost(args)
.Run()
}
}
Да, сагу можно развернуть в набор конструкций с try-catch, но это будет выглядеть довольно громоздко, когда необходимо сделать значительное количество шагов. Вот, например, развернутая сага из трех шагов:
По своей сути сага в нашей библиотеке содержит лишь довольно простой алгоритм для вызова действий в определенной последовательности, не более. Поэтому на обеспечение консистентности работают уже конкретные реализации этих действий.
Например, у нас есть проект, где в рамках бизнес-процесса задействуются 6 сервисов, в каждом сервисе используется модель хранения данных в виде событий, а значит, что для изменнения данных в рамках одного сервиса делается только insert в БД. В этом случае сага состоит из 6-ти основных шагов, где выполняются локальные доменные действия и делаются insert-ы событий во временные таблицы. Далее в этой же саге выполняется еще 6 шагов, где созданные записи переносятся в основные таблицы. В случае отката, например, если на шаге 4 не пройдет валидация данных, события из временных таблиц сервисов 1-3 будут удалены. По сути это некая реализация двухфазного коммита. В качестве транспорта используется RabbitMQ с настройками, гарантирующими доставку сообщения. Так что, консистентность здесь вполне достижима.
По поводу оберток и зависимостей. Да, мы это понимаем, поэтому у нас есть задачи в бэклоге, которые нацелены как раз на переход к нативным механизмам и абстракциям платформы (например, тот же ILogger), а также на более глубокую декомпозицию функционала.
github.com/Raiffeisen-DGTL/ViennaNET/blob/master/ViennaNET.WebApi.Configurators.Swagger/SwaggerConfigurator.cs
Наш фреймворк делался для запуска рестовых сервисов, а не для бэкграунд воркеров.
Немного дополню по поводу Http-клиентов. Вот, пример реализации для JWT-клиента: github.com/Raiffeisen-DGTL/ViennaNET/blob/master/ViennaNET.WebApi.Configurators.HttpClients.Jwt/JwtHttpClientsConfigurator.cs. По сути здесь мы просто добавляем хэндлер для переотправки входящего токена.
В подобных конфигураторах можно сделать более сложную логику, добавить другие хэндлеры, считывать дополнительные параметры из файла конфигурации. Ну и затем, через свой метод расширения, подключить:
— Нет. Медиатор — это внутренняя шина, по которой можно отправлять сообщения или команды, на которые будут реагировать обработчики. Таким образом проще соблюдать SRP, ведь каждый обработчик отвечает только за обработку одной конкретной команды.
Это по сути для «пустого» сервиса. Полноценный пример будет позже в репозитории.
Program.cs:
Конфиг conf/appsettings.json: