Pull to refresh
7
0
Send message
Спасибо за комментарий.
Да, сагу можно развернуть в набор конструкций с try-catch, но это будет выглядеть довольно громоздко, когда необходимо сделать значительное количество шагов. Вот, например, развернутая сага из трех шагов:
Пример кода
var context = new ExampleContext();

try
{
  await Step1Action(context);
}
catch (Exception ex)
{
}

try
{
  await Step2Action(context);
}
catch (Exception ex)
{
  await RollbackStep1(context);
}

try
{
  await Step3Action(context);
}
catch (Exception ex)
{
  await RollbackStep2(context);
  await RollbackStep1(context);
}


По своей сути сага в нашей библиотеке содержит лишь довольно простой алгоритм для вызова действий в определенной последовательности, не более. Поэтому на обеспечение консистентности работают уже конкретные реализации этих действий.
Например, у нас есть проект, где в рамках бизнес-процесса задействуются 6 сервисов, в каждом сервисе используется модель хранения данных в виде событий, а значит, что для изменнения данных в рамках одного сервиса делается только insert в БД. В этом случае сага состоит из 6-ти основных шагов, где выполняются локальные доменные действия и делаются insert-ы событий во временные таблицы. Далее в этой же саге выполняется еще 6 шагов, где созданные записи переносятся в основные таблицы. В случае отката, например, если на шаге 4 не пройдет валидация данных, события из временных таблиц сервисов 1-3 будут удалены. По сути это некая реализация двухфазного коммита. В качестве транспорта используется RabbitMQ с настройками, гарантирующими доставку сообщения. Так что, консистентность здесь вполне достижима.

По поводу оберток и зависимостей. Да, мы это понимаем, поэтому у нас есть задачи в бэклоге, которые нацелены как раз на переход к нативным механизмам и абстракциям платформы (например, тот же ILogger), а также на более глубокую декомпозицию функционала.
Насколько я понимаю, это делается в настройках SwaggerGen. Его можно сконфигурировать через метод расширения UseSwaggerWithOptions() из конфигуратора
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. По сути здесь мы просто добавляем хэндлер для переотправки входящего токена.

В подобных конфигураторах можно сделать более сложную логику, добавить другие хэндлеры, считывать дополнительные параметры из файла конфигурации. Ну и затем, через свой метод расширения, подключить:
Скрытый текст
CompanyHostBuilder.Create()
                  ...
                  .UseCustomJwtClient()
                  ...
                  .BuildWebHost(args)
                  .Run()



Это про то, что теперь вместо пяти разных сервисов, можно заинжектить какой-нибудь IMediator и юзать его?
— Да

Если да, то есть мнение, что это тот же service locator.
— Нет. Медиатор — это внутренняя шина, по которой можно отправлять сообщения или команды, на которые будут реагировать обработчики. Таким образом проще соблюдать SRP, ведь каждый обработчик отвечает только за обработку одной конкретной команды.
Да, пример, не выложили…
Это по сути для «пустого» сервиса. Полноценный пример будет позже в репозитории.
Program.cs:
class Program
{ 
	public static void Main(string[] args)
	{
		CompanyHostBuilder.Create()
                                  .UseServer((b, c) => { b.UseKestrel(); })
                                  .BuildWebHost(args)
                                  .Run()
	}
}

Конфиг conf/appsettings.json:
{
  "webApiConfiguration": {
	"portNumber": 80
  }
}

Information

Rating
Does not participate
Works in
Registered
Activity