TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.74K subscribers
Post #3207 1.72K
День 2679. #ЗаметкиНаПолях
Когда Сценарий Выполняется Наполовину: Проектирование с Учётом Частичного Сбоя в .NET. Начало

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

Код, который выглядит нормально
Вот типичный сценарий «размещения заказа»:
internal sealed class PlaceOrder(
IOrderRepository orders,
IPaymentService payments,
IEmailService email,
IUnitOfWork uow)
{
public async Task<Result> ExecuteAsync(
PlaceOrderRequest or, CancellationToken ct)
{
var order = Order.Create(or.CustomerId, or.Items);
orders.Insert(order);

await payments
.ChargeAsync(order.Id, order.Total, ct);

await email
.SendOrderConfirmationAsync(order.Id, ct);

await uow.SaveChangesAsync(ct);
return Result.Success();
}
}

В этом методе есть три побочных эффекта, скрытых за, казалось бы, единой транзакционной границей, без какой-либо координации между ними.

Если SaveChangesAsync вызывает исключение после успешного выполнения ChargeAsync, вы списали деньги клиента и потеряли заказ. Если SendOrderConfirmationAsync вызывает исключение, заказ сохраняется, и платёж проходит, но email не отправляется. А если вы попытаетесь повторить попытку, вы спишете деньги дважды. Этот вариант использования «работает», пока не ломается, а когда ломается, он, как правило, каждый раз завершается с разной ошибкой.

Три категории побочных эффектов
Прежде чем написать хотя бы одну строку кода восстановления, классифицируйте каждый побочный эффект по одной из 3 категорий:

- Транзакционные — находятся внутри транзакции вашей БД. Вставки, обновления, события домена, отправляемые в процессе.
- Внешние и обратимые — вызов API, который вы можете компенсировать. Платёж → возврат. Резервирование запасов → освобождение.
- Внешние и необратимые — отправленные email, веб-хуки, SMS-сообщения. Как только они отправлены, их не вернуть.

Категория определяет стратегию. Не существует единого правила «правильной обработки ошибок», которое охватывало бы все три.

Окончание следует…

Источник:
https://www.milanjovanovic.tech/blog/when-your-use-case-half-succeeds-designing-for-partial-failure-in-dotnet
  • 👍 9
  • 👎 1
More from @netdeveloperdiary
  1. Sep 26, 2026День 2796. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Продолжение Начало Три…
  2. Sep 25, 2026День 2795. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Начало Проблема с позиц…
  3. Sep 24, 2026День 2794. #Оффтоп #Здоровье Сегодня будет необычный пост. Завтра в Москве стартует конфер…
  4. Sep 23, 2026День 2793. #ЗаметкиНаПолях #SQL 10 Редких Возможностей SQL, Которые Стоит Знать Каждому. Ч…
  5. Sep 22, 2026День 2792. #ЗаметкиНаПолях #SQL 10 Редких Возможностей SQL, Которые Стоит Знать Каждому. Ч…
  6. Sep 21, 2026🔍Тестовое собеседование с Senior C# разработчиком уже завтра 22 сентября(уже завтра!) в 1…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →