В atproto нет объектов-экземпляров
Каждый раз, когда на Hacker News всплывает пост про ATProto, кто-то обязательно спрашивает: «А где все инстансы Bluesky?». И каждый раз кто-то объясняет: их нет. Эта фраза звучит странно, если ты привык к миру Mastodon или обычному объектно-ориентированному программированию.
Суть проблемы — категориальная ошибка. Мы пытаемся уложить новую распределённую систему в старую ментальную модель и удивляемся, почему не складывается.
Давайте разберёмся, что именно означает «нет инстансов» и почему это не недостаток, а осознанное архитектурное решение.
Хостинг и агрегация — это разные вещи
Вспомним эпоху RSS и Google Reader. Это был золотой век личных блогов:
Блог Алисы Блог Кота Блог Боба
│ │ │
└─────────────┼────────────┘
▼
┌─────────────────┐
│ Google Reader │
│ Feedly │
└─────────────────┘
Посты публикуются на своём собственном сервере (хостинг), а приложения типа Google Reader или Feedly просто собирают ленту из всех подписок (агрегация). Ваши записи не «живут» в Feedly — приложение лишь проецирует данные из блогосферы.
Это разделение критически важно понять: хостинг ≠ агрегация.
Эволюция: Facebook и другие силосы
┌──────────────────────────────────┐ │ Facebook / Twitter / etc │ │ │ │ [Запись] [Запись] [Запись] │ │ ▲ │ │ └── Единственное место │ └──────────────────────────────────┘
Компания берёт всю эту красоту, оборачивает в коробку ради рекламы и оставляет только одно приложение (альтернативные клиенты живут недолго). Твои данные существуют только внутри платформы.
ATProto делает шаг назад к модели RSS — но с современным уровнем сложности.
Что такое «нет инстансов» на практике?
В традиционной системе у тебя есть объект:
// Привычный мир OOP/ORM
const user = await User.findById(123);
user.name = "Новое имя";
await user.save();
Здесь user — это объект-экземпляр с идентичностью во времени.
В ATProto всё иначе. Данные адресуются через записи (records) по URI:
at://did:plc:abc123/app.bsky.feed.post/xyz789
├── Протокол (at)
├── Идентификатор аккаунта (did:plc:abc123)
├── Коллекция записей (app.bsky.feed.post)
└── Уникальный ключ внутри коллекции (xyz789)
АДРЕС ЗАПИСИ В ATPROTO ─────────────────────── at://did:plc:megatrack/app.bsky.feed.post/k3l44j8q at:// ← Протокол did:plc:mega... ← Кто создал (Decentralized ID) app.bsky... ← Тип данных k3l44j8q ← Конкретный экземпляр записи
Что тут принципиально важно?
Запись immutable — она либо есть, либо её нет. Если ты хочешь её изменить, ты создаёшь новую версию со своей историей изменений (com.atproto.repo.listRecords).
Не существует «живого объекта», который можно обновить in-place. Есть только адрес данных.
Репозиторий как источник данных
Каждый пользователь владеет репозиторием — хранилищем всех своих записей:
РЕПОЗИТОРИЙ ПОЛЬЗОВАТЕЛЯ DID:PLCLIVEKITTY ═════════════════════════════════════════ Репозиторий ──▶ Коллекции записей ┌─────────────────────────────────────┐ │ did:plc:livekitty │ ├─────────────────────────────────────┤ │ app.bsky.feed.post/ │ │ ├── k3l44j8q │ │ ├── b7qp9mnx │ │ └── r1z8v2kl │ ├─────────────────────────────────────┤ │ app.bsky.actor.profile/ │ │ └── self │ └─────────────────────────────────────┘ Адрес профиля: at://did:plc:livekitty/app.bsky.actor.profile/self
Ты можешь запросить любую запись напрямую по URI без привязки к конкретному серверу или «инстансу». Репозиторий самодостаточен — он знает всё о своих данных.
Почему это лучше для распределённых систем?
Традиционные инстансы Mastodon требуют сложной синхронизации между серверами при federation:
- Кто кого знает?
- Где живёт пользователь X?
- Как синхронизировать ленту?
В ATProto вопрос «где?» решается через DID (did:) — децентрализованный идентификатор. Аккаунт не привязан к домену сервера!
## Mastodon:
// mastodon.social/users/alice <- зависит от сервера
## Atproto:
// did:plc:megatrack <- независимый идентификатор через реестр PLC!
При смене хостинга DID остаётся неизменным, а данные мигрируют между репозиториями прозрачно.
Результат: federation становится проще математически — ты всегда знаешь «чей этот контент» по URI без дополнительного lookup’а.
Выводы для практики
- Забудь про объекты как сущности с состоянием. Есть только immutable-записи по фиксированным адресам.
- Думай через URI. Вместо
user.update({name})работай сPUT at://.../profile/self. - Разделяй аккаунт (= DID) и хостинг (= где лежит repo). Это позволяет менять провайдера без потери идентичности.
- Приложения — проекции. Bluesky.app или любой другой клиент лишь читает твой repo; твои данные там не живут.
Эта модель странная для тех, кто вырос на ORM’ах и монолитных API-шках. Но именно она делает возможной настоящую federated соцсеть без координационного хаоса между серверами.
Знание того, что «инстансов нет», экономит час спора на Hacker News.
Ссылки
- There are no instances in ATProto — оригинальная статья о том, почему в ATProto нет инстансов
Дмитрий Полухин — продуктовый дизайнер. Пишу про разработку, AI и дизайн интерфейсов. Обо мне, контакты и профили.