Files
forkmessager/backend/src/Docs/stories_module_documentation.md
2026-03-23 01:43:27 +03:00

9.7 KiB
Raw Blame History

Модуль Историй (Stories) - Техническая Документация

1. Обзор

Модуль Историй отвечает за создание, просмотр и предоставление ленты временных публикаций. Любая история доступна для просмотра в ленте ровно 24 часа с момента её публикации, после чего автоматически исчезает. Модуль построен на архитектуре CQRS с использованием паттерна MediatR, а публичный REST API реализован через Minimal APIs с помощью библиотеки Carter.

Важная деталь предметной области: Ответы на истории и быстрые реакции обрабатываются модулем Conversations/Messaging и доставляются автору в личные сообщения (Direct Messages) как специальный тип сообщения (StoryMessage). Сам модуль Stories отвечает исключительно за контент, жизненный цикл и учет просмотров истории.

2. Гибридная Архитектура Хранения

Для обеспечения отказоустойчивости при масштабировании, модуль использует Гибридную модель данных, распределяя нагрузку между двумя базами:

A. MongoDB (Документное хранилище)

Используется как основное хранилище сверхбыстрых данных документа Story (IStoryRepository).

  • Молниеносное чтение: Отсутствие тяжелых SQL-связей (джоинов) позволяет мгновенно формировать ленты историй для пользователей. Актуальность истории вычисляется автоматически при выборке путем сравнения времени создания с текущим.
  • Атомарные операции: Счетчик просмотров (ViewsCount) обновляется быстрой In-Place атомарной операцией $inc, что полностью исключает блокировки записей (locking) при массовых просмотрах.
  • Нативное шифрование (E2EE): Чувствительные текстовые поля (такие как Content и MediaUrl) автоматически и прозрачно шифруются алгоритмом AES-256 прямо перед записью в базу через кастомный EncryptedStringSerializer, внедрённый на уровне BSON-маппинга.

B. PostgreSQL (Реляционная база данных)

Используется для хранения точных логов просмотров StoryViewers (через StoriesDbContext).

  • Точечный контроль: Хранение идентификаторов уникальных зрителей (ViewerId) в реляционной БД предотвращает раздувание BSON-документов MongoDB.
  • Строгая целостность: Таблица просмотров содержит составной уникальный индекс (StoryId, UserId), что на уровне самой базы гарантирует защиту от "накрутки" — один человек засчитывается в просмотры истории только один раз.

3. Модели Данных

Документ Story (в MongoDB)

  • Id (Guid): Уникальный внутренний ключ.
  • UserId (Guid): Создатель истории.
  • Type (StoryType Enum): Тип контента: Text = 0, Image = 1 или Video = 2.
  • MediaUrl (string, зашифровано!): Ссылка на привязанный S3-файл.
  • Content (string, зашифровано!): Содержимое текстовой истории или подпись к медиа.
  • BgColor (string, опционально): Цвет фона для текстового типа.
  • CreatedAt (DateTime): Точка отсчета жизни истории. На основе этого времени "на лету" вычисляется активность истории (обычно CreatedAt > DateTime.UtcNow.AddHours(-24)).
  • ViewsCount (int): Предвычисленный быстрый счетчик уникальных просмотров, готовый к мгновенной выдаче клиентам.

Сущность StoryViewer (в PostgreSQL)

  • Id (Guid): Первичный ключ записи.
  • StoryId (Guid): Внешний условный ключ профиля истории.
  • UserId (Guid): Идентификатор посмотревшего.
  • ViewedAt (DateTime): Точная временная метка факта просмотра.

4. Хранение Файлов и Безопасность

  • MinIO S3 Хранилище: Загрузка тяжелого медиа производится через смежный модуль Storage. Файлы именуются по алгоритму SHA-256 (от содержимого файла), что нативно гарантирует абсолютную дедупликацию мемов и роликов на серверах.
  • Шифрование данных (At-Rest): Все текстовые данные, попадающие в Mongo, зашифрованы сервисом IEncryptionService. Дамп базы данных не имеет ценности без ключей окружения приложения.

5. Описание API Эндпоинтов (api/stories)

Метод Маршрут Описание
GET / Получение гибридной ленты актуальных историй текущих друзей (Mongo+Postgres маппинг).
GET /user/{userId} Запрос актуальных историй конкретного активного пользователя.
POST / Создание и мгновенная публикация новой истории.
DELETE /{id} Удаление собственной истории автором в любой момент времени.
POST /{id}/view Фиксация просмотра. Добавляет запись в PostgreSQL и инкрементит MongoDB-счетчик.
GET /{id}/viewers Получение списка зрителей истории (функция "свайп вверх" для авторов).

6. Real-Time Интеграция (SignalR WebSockets)

Модуль историй поддерживает мгновенное оповещение авторов через WebSockets, встроенное в ViewStoryCommand. Логика прямого эфира (когда Юзер B просматривает историю Юзера A):

  1. Запрос валидируется через PostgreSQL (ранее не просмотрено).
  2. Выполняется мгновенный инкремент $inc счетчика в MongoDB.
  3. Через ChatHub адресно автору истории моментально отправляется событие story_viewed.

Спецификация JSON-события "Мою историю просмотрели":

{
    "storyId": "guid",
    "userId": "guid",           // Кто именно сейчас посмотрел
    "username": "johndoe",
    "displayName": "John Doe",
    "avatar": "url...",
    "viewedAt": "timestamp",
    "viewCount": 42,            // Актуальный живой счетчик на этот момент!
    "ownerId": "guid"           // Кому адресован ивент (User A)
}

Фронтенд может слушать событие story_viewed и анимированно обновлять счетчики "в прямом эфире", а также пополнять список зрителей в попапе истории без дополнительных HTTP-запросов.