Что такое паттерн аутбокс и зачем он нужен?
Что проверяют
Ту же идею, но с уклоном в реализацию на платформе: куда в цепочке сохранения вклинить запись события, чем крутить отправку в фоне и как не получить дубли при нескольких инстансах. Определение здесь проходной балл, разговор быстро смещается к деталям.
Как отвечать
Суть в одной фразе: атомарно сохранить изменения в базе и опубликовать сообщение в брокер нельзя, поэтому событие записывается в ту же базу тем же сохранением, а отправка выносится в отдельный процесс.
На практике это выглядит так. Переопределяем сохранение контекста данных: собираем доменные события агрегатов и добавляем строки в таблицу исходящих в рамках той же транзакции. Фоновый сервис хостинга берёт пачку неотправленных строк, публикует и проставляет отметку обработки. Готовые реализации транзакционного аутбокса есть в популярных шинах сообщений, и упомянуть это полезно: свой код пишут, когда нужен контроль над порядком, чисткой и схемой таблицы.
Что спросят следом:
- блокировка строк при нескольких инстансах: пропуск заблокированных записей в PostgreSQL, чтение без ожидания в MS SQL;
- повторы и отравленные сообщения: счётчик попыток, задержка между ними, отдельная таблица для безнадёжных;
- идемпотентность потребителя по идентификатору сообщения;
- рост таблицы и её чистка.
Где спотыкаются
Переворачивают логику: описывают аутбокс как запасной путь, куда сообщение кладут, если брокер не ответил. Тогда падение между коммитом и попыткой отправки снова теряет событие. Строка пишется всегда и до публикации, внутри транзакции с бизнес-изменением.
Пишут строку правильно, но публикуют прямо в обработчике сразу после коммита и считают задачу решённой. Отправка без ожидания результата не переживает перезапуск контейнера, а строка остаётся неотправленной навсегда, если её никто не перечитывает.
Забывают про принимающую сторону. Гарантия здесь не меньше одного раза, а не ровно один, и без хранения обработанных идентификаторов повтор превращается в двойное начисление или второе письмо клиенту.