Event Contract: pin.favorites.favorite-removed (FavoriteRemovedKafkaEvent)
Версионируемый контракт интеграционного события «объект недвижимости удалён из избранного».
Consumer-документы ссылаются сюда, не переописывая payload. Дом полей-источников —
entity: Favorite.
Назначение
Фиксирует явное удаление объекта недвижимости из избранного владельцем (пользователем или гостевой сессией).
Единственный потребитель — user-activity (модуль actions, ADR-0054): ActionEvent «удалил из
избранного» в историю действий и — через поток user-activity → crm — в карточку лида (агенту важно
видеть и охлаждение интереса к объекту недвижимости, не только добавления).
Metadata
| Поле | Значение |
|---|---|
| Event type | pin.favorites.favorite-removed |
| C# type | FavoriteRemovedKafkaEvent (проект Pin.Favorites.Contracts) |
| Topic | pin.favorites.favorite-removed.v1 |
| Partitions / key | 6 партиций / key = AggregateId = FavoriteId — remove попадает в ту же партицию, что и add той же записи (одинаковый FavoriteId), порядок «add → remove» сохраняется |
| ContractVersion | 1 |
| Producer | favorites: DELETE /favorites/{propertyObjectId} |
| Owning team | PIN Engagement |
| Статус | draft |
Событие публикуется атомарно с физическим удалением записи Favorite (Favorite — без soft delete,
см. favorite.md). Ключ дедупликации у потребителя — идентификатор сообщения
(MessageId); ключ партиционирования — FavoriteId (AggregateType = "Favorite").
Payload FavoriteRemovedKafkaEvent
| Поле | Тип | Обязательность | Источник (поле агрегата) | Compatibility | Описание |
|---|---|---|---|---|---|
FavoriteId | Guid | Да | Favorite.Id (снимок до удаления) | Immutable | Идентификатор удалённой записи; совпадает с FavoriteId её события favorite-added — consumer может связать пару add/remove. |
UserId | Guid? | Условно (XOR c AnonymousId, INV-FAV-001) | Favorite.UserId | Immutable | Владелец-пользователь; null — гостевая запись. |
AnonymousId | Guid? | Условно (XOR c UserId) | Favorite.AnonymousId | Immutable | Гостевая сессия-владелец; null — запись пользователя. |
PropertyObjectId | Guid | Да | Favorite.PropertyObjectId | Immutable | Объект недвижимости, удалённый из избранного. |
OccurredAt | DateTimeOffset | Да | момент выполнения удаления (now) | Immutable | Момент удаления (запись физически удалена — timestamp-полей у неё больше нет). |
Инвариант payload: заполнен ровно один из UserId / AnonymousId (проекция INV-FAV-001);
нарушение — error-лог consumer-а + skip. Note удалённой записи в payload не входит (приватные
данные; для истории действий текст заметки не нужен). Вложенных моделей нет.
Когда публикуется
| Условие | Переход состояния |
|---|---|
DELETE /favorites/{propertyObjectId} — запись владельца найдена (INV-FAV-005) и физически удалена; повторный DELETE несуществующей записи — идемпотентный успех без события | Favorite: существует → удалена (физически) |
Событие не публикуется при:
- удалениях внутри слияния (
AccountWins— все S-записи;Merge— S-дубли;DeviceWins— A-записи): итог покрыт однимpin.favorites.favorites-merged.v1со счётчиками — веер remove-событий создал бы ложные ActionEvent «пользователь удалил» для технической дедупликации (ADR-0056); - retention-чистке гостевых записей job-ом
guest-favorites-cleanup(365 дней неактивности) — системная уборка не является действием пользователя, истории действий она не нужна; job удаляет батчами без доменных и интеграционных событий.
Потребители (consumers)
| Consumer-контекст | Эффект (side effects) | Идемпотентность | Задержка (SLA) |
|---|---|---|---|
user-activity (будущий док: docs/06-services/user-activity/consumers/favorites-actions.md) | ActionEvent «удалил из избранного» в историю действий; проброс в карточку лида — поток user-activity → crm | MessageId (дубль — skip) | < 1 мин |
CRM напрямую не подписан (см. favorite-added.md — та же схема).
Гарантии доставки, порядок и восстановление
- Доставка — at-least-once. Публикация атомарна с физическим удалением записи
Favorite: удаление произошло → событие гарантированно доставится; изменение откатилось → события нет. Дубликаты доставки дедуплицируются потребителем поMessageId. - Порядок — внутри партиции по
FavoriteId.favorite-removedпопадает в ту же партицию, что иfavorite-addedтой же записи (общий ключ), поэтому у потребителя remove не может опередить add одной записи. Между разными записями порядок не гарантирован — хронология поOccurredAt. - Восстановление у потребителя. Транзиентный сбой обработки — retry до успеха; нарушение
XOR-инварианта payload (INV-FAV-001) — error-лог + skip (невосстановимо, не в DLQ); poison — в DLQ по
политике потребителя. Replay безопасен: ActionEvent-проекция идемпотентна по
MessageId. - Окно согласованности. Между
DELETE /favorites/{propertyObjectId}(пользователь уже видит снятое «сердечко») и ActionEvent «удалил из избранного» в истории/карточке лида — < 1 мин; недоступность шины добавление/удаление не блокирует.
Версионирование и совместимость
ContractVersion = 1; несовпадение версии контракта — warn-and-proceed.- Additive-only — новые optional-поля без bump; breaking —
ContractVersion = 2+ топикpin.favorites.favorite-removed.v2+ dual-publish + sunset.v1. - Enum-полей нет — unknown enum не применяется.
- Событие — факт: дедупликация по
MessageId, replay безопасен (ActionEvent-проекция идемпотентна поMessageId).
Безопасность
| Поле | Класс данных | Правило |
|---|---|---|
FavoriteId, PropertyObjectId, OccurredAt | Internal | include |
UserId, AnonymousId | Internal | include |
Note удалённой записи | Приватная заметка владельца | exclude — не публикуется |
Событие НЕ содержит secrets, tokens, PII-профиля, текста заметок.
Обратные ссылки
Автоссылки: где используется этот документ.
- API (1): DELETE /api/v1/favorites/{propertyObjectId} — Удалить объект недвижимости из избранного
- События (2): Event Contract: pin.favorites.favorite-added (FavoriteAddedKafkaEvent), Event Contract: pin.favorites.favorites-merged (FavoritesMergedKafkaEvent)
- Консюмеры (1): Consumer: Favorites Events (UserActivityFavoriteAddedConsumer / ...RemovedConsumer)
- index.md (1): Favorites Service (Pin.Favorites)
- registries (1): Реестр событий PIN
Связанные документы
- Источник полей: entity:
Favorite(INV-FAV-001, INV-FAV-005). - Парные события: favorite-added.md, favorites-merged.md.
- Границы: ../README.md; consumers контекста: ../consumers/README.md.
- Решения: ADR-0054, ADR-0056.