Ежедневные дайджесты без дублей: кейс на .NET
Рабочий кейс из enterprise-разработки на .NET с использованием библиотеки Hangfire
В программном модуле пользователи создают объект (Header) и наполняют его строками (Items). Изменения Header и Items внутри Header логируются.
Появилась потребность получать по email ежедневные дайджесты: • Обо всех действиях с Items, которые происходят внутри конкретного Header; • Об изменениях конкретного Item. независимо от Header, к которому они принадлежат.
КЛЮЧЕВЫЕ ЗАДАЧИ: • расширяемость под новые события • логирование отправленных писем • идемпотентность – письмо с дайджестом за день должно отправиться один раз, без дублей • асинхронная архитектура
ДЛЯ РЕШЕНИЯ ЗАДАЧИ ПОСТРОИЛ 5 ПОДСИСТЕМ: 1. Подсистема хранения и предоставления событий изменений.
2. Подсистема управления подписками пользователей на уведомления с поддержкой как привязки к конкретным сущностям (Header, Item), так и глобальных событий. Расширяемость обеспечивается через справочник типов уведомлений.
3. Подсистема обработки уведомлений, реализованная через паттерн Strategy, позволяющая динамически выбирать обработчик по типу события и изолировать логику формирования и отправки писем.
4. Подсистема идемпотентной отправки уведомлений на основе детерминированного hash-ключа и журнала отправок, обеспечивающая отсутствие дублей и аудит статусов.
5. Подсистема оркестрации, управляющая жизненным циклом отправки уведомления (от генерации hash-ключа до фиксации результата), выполняется асинхронно через Hangfire с поддержкой ретраев и независимой обработки задач.
СИСТЕМА ОБЕСПЕЧИВАЕТ: 1. гарантию отсутствия дублирующих уведомлений для одного пользователя в рамках заданного периода;
2. возможность добавления новых типов уведомлений без изменения существующей логики обработки;
3. неблокирующую обработку уведомлений за счёт вынесения в фоновые задачи;
4. горизонтальную масштабируемость за счёт увеличения количества фоновых обработчиков.
При исчерпании стандартных retry-механизмов требуется отдельная стратегия повторной обработки (manual retry или дополнительные фоновые процессы).
ОСНОВНОЙ ИНСАЙТ: ключевыми факторами устойчивости решения стали идемпотентность и разделение ответственности между компонентами.
Интересно, как вы решаете задачу доставки и дедупликации уведомлений?