ADR-0054: Сессии + история просмотров + трекинг действий = один контекст user-activity
Статус
Accepted
Дата
2026-07-13
Контекст
ТЗ.md описывает три пересекающихся сервиса и само сомневается в границах (стр. 267: «нужен ещё трекинг-сервис… или это должна быть его часть, его модуль»): Сервис сессий (263–273), Сервис истории просмотров (253–261), Сервис отслеживания действий (359–361). Все трое работают с одним потоком данных: гостевая/пользовательская сессия → события действий (просмотры с длительностью, фильтры, избранное, подборки) → выдача истории пользователю/агенту/в карточку лида.
Решение
Один bounded context user-activity с тремя модулями:
| Модуль | Владеет | Отдаёт |
|---|---|---|
sessions | Session, anonymous_id, связывание сессия↔пользователь при входе | контекст сессии для favorites/каталога |
views | ViewHistory (объект, фильтр-контекст, длительность просмотра) | история просмотров пользователю; по UserId — агенту/менеджеру |
actions | ActionEvent (typed: применён фильтр, добавлено в избранное/подборку, поиск) | события в карточку лида (CRM), будущий вход рекомендаций news |
Один API/один store — только PostgreSQL (ADR-0056: simplicity-first, без Redis/ClickHouse), общие
события pin.user-activity.*. Закрытие сессий/просмотров и длительности — механизм
LastActivityAt + polling-delta (10–15 мин) с публикацией session-ended/lot-view-ended и
агрегатами LotViewStats — см. ADR-0056 п.3. Разделение на отдельные сервисы возможно позже без смены
контрактов.
Последствия
- Устранено дублирование моделей сессии/события в трёх местах; «слияние сессий» избранного использует единый механизм связывания.
- CRM получает один consumer потока действий для карточки лида (реф-требование «отслеживание в течение месяца»).
- Приватность: доступ к чужой истории — только по permissions (агент видит историю лида в рамках активной связи; ТЗ.md:257–259).
Обратные ссылки
Автоссылки: где используется этот документ.
- Домен (CQRS) (1): Entity / Aggregate: PostReaction + проекция PostRankingSignals (Design)
- API (8): DELETE /api/v1/subscriptions/{id} — Отписаться от источника, GET /api/internal/user-activity/users/{userId}/property-object-activity — Активность инвестора по объектам недвижимости (карточка лида CRM), GET /api/internal/user-activity/users/{userId}/property-objects/view-stats — Батч просмотров инвестора по объектам недвижимости (обогащение карточек CRM), GET /api/v1/posts/{idOrSuffix} — Чтение опубликованного поста, GET /api/v1/subscriptions — Мои подписки, POST /api/user-activity/sessions/heartbeat — Heartbeat активности сессии, POST /api/v1/posts/{id}/publish — публикация поста (snapshot + событие), POST /api/v1/subscriptions — Подписаться на автора/компанию
- Use Cases (4): UC-NWS-001. Создать черновик и опубликовать пост, UC-NWS-003. Вложенные переключаемые комментарии, UC-NWS-004. Подписки и лента subscription, UC-NWS-005. Поиск постов (Elasticsearch)
- События (1): Event Contract: pin.news.post-published (PostPublishedKafkaEvent)
- Консюмеры (2): Consumer: Favorites Events (UserActivityFavoriteAddedConsumer / ...RemovedConsumer), Consumer: Selections Events (UserActivitySelectionItemAddedConsumer)
- Фоновые задачи (1): Background Job: CommentCountReconcileJob
- index.md (3): DevStats Service (Pin.DevStats), News Service (Pin.News), user-activity Service
Связанные документы
docs/06-services/user-activity/,docs/06-services/favorites/(слияние),docs/06-services/crm/(карточка лида).