9.7 KiB
Модуль Историй (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):
- Запрос валидируется через PostgreSQL (ранее не просмотрено).
- Выполняется мгновенный инкремент
$incсчетчика в MongoDB. - Через
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-запросов.