Дельта android-live-albums: android-client (+3 требования: живое обновление списка, догон при переподключении, потеря доступа) и realtime (порядок списка теперь правило отображения клиента). Дельта fix-album-cover-readiness: realtime (+1 требование: album.updated при готовности вариантов фото-обложки).
66 lines
5.1 KiB
Markdown
66 lines
5.1 KiB
Markdown
# Design
|
||
|
||
## Context
|
||
|
||
См. `proposal.md` — мотивация. Что важно для подхода:
|
||
|
||
- Ссылки на обложку формируются из `photo_objects` (роли `thumb`/`medium`), которые
|
||
заполняются асинхронно, поэтому в объекте альбома (`Store.GetByID` джойнит обложку) они
|
||
появляются не в момент загрузки, а когда варианты готовы.
|
||
- Загрузка фото вызывает `SetCoverIfEmpty`, то есть первое фото становится обложкой ещё до
|
||
генерации вариантов, а `NotifyAlbumChanged` на этом шаге публикует `album.updated` уже с
|
||
`cover_photo_id`, но с пустыми ссылками.
|
||
- Готовность вариантов обрабатывает `Worker.notify` (`internal/photos/worker.go`): он
|
||
публикует только `photo.updated` в канал альбома и персональные потоки не трогает.
|
||
- `albums.Service` уже умеет перечитать альбом, обогатить обложку (`attachCover`) и веерно
|
||
опубликовать `album.updated` (`NotifyAlbumChanged` → `PublishAlbumTo`).
|
||
|
||
## Goals / Non-Goals
|
||
|
||
**Goals:**
|
||
|
||
- Доставить актуальную ссылку на обложку в персональные потоки, когда она появляется.
|
||
- Не увеличивать поток событий при массовой загрузке.
|
||
|
||
**Non-Goals:**
|
||
|
||
- Менять контракт `photo.updated` в канале альбома (открытый экран закрывает свой случай сам).
|
||
- Клиентские обходные пути (рефетч списка по таймеру и т.п.).
|
||
- Синхронная генерация вариантов при загрузке.
|
||
|
||
## Decisions
|
||
|
||
**1. Публикует `albums.Service`, а не воркер.**
|
||
Решение «объект альбома изменился» принимает владелец альбомов: добавляется
|
||
`NotifyCoverReady(ctx, albumID, photoID)`, который перечитывает альбом и публикует
|
||
`album.updated`, только если `cover_photo_id == photoID` (иначе no-op), переиспользуя
|
||
`NotifyAlbumChanged`. Воркер остаётся про фото и лишь сообщает о готовности.
|
||
Альтернатива — строить `album.updated` прямо в воркере: тогда `photos` пришлось бы знать
|
||
форму объекта альбома, обложку и аудиторию.
|
||
|
||
**2. Условие — совпадение с текущей обложкой.**
|
||
Готовность вариантов обычного фото объект альбома не меняет. Без условия пришлось бы на
|
||
каждое фото перечитывать альбом и веерно публиковать `album.updated` всем участникам — лишняя
|
||
нагрузка при массовой загрузке и лишние события клиентам.
|
||
Альтернатива — «публиковать всегда»: проще, но шумно; отвергнуто.
|
||
|
||
**3. Проверка по `cover_photo_id`, а не по «в альбоме ещё нет ссылки на обложку».**
|
||
Так закрываются оба сценария: и автопоставленная обложка первой загрузкой, и явная установка
|
||
обложки на фото, варианты которого ещё не готовы.
|
||
|
||
## Risks / Trade-offs
|
||
|
||
- **Двойное чтение альбома** (`GetByID` для проверки обложки и внутри `PublishAlbumTo`) →
|
||
дополнительное чтение точечное (только для готовности вариантов фото-обложки), принято; при
|
||
необходимости схлопывается в один публикующий вызов.
|
||
- **Гонка со сменой обложки** между публикацией `photo.created` и готовностью вариантов →
|
||
проверка по актуальному `cover_photo_id` отсекает лишнюю публикацию, а новая обложка получит
|
||
своё событие при готовности её вариантов.
|
||
- **Аудитория недоступна** (ошибка в `members.Audience`) → событие не публикуется; клиент
|
||
догоняет состояние при переподключении (`refresh`) — существующее поведение веера.
|
||
|
||
## Migration Plan
|
||
|
||
Изменение аддитивное: схема и API не меняются, нужен только перезапуск бэкенда (логика и
|
||
статика в одном бинарнике). Откат — предыдущий бинарник.
|