Skip to Content
Каталог событий

Event catalog

Каталог Kafka-топиков. Одна запись на топик: как называется, кто publisher, кто consumer’ы, где лежит payload.

Эта страница — directory. Cross-service контракт — это envelope (поля metadata) и правила naming / versioning. Детальный payload каждого топика и история Schema-Version живут в репо publisher’а (docs/events.md — см. conventions/service-readme).

Связка с services-catalog: сервис в каталоге перечисляет префиксы своих топиков; payload — в репо того сервиса.

Содержание

Конвенции

Все события следуют единым правилам. Полный разбор Watermill / outbox — в conventions/events.

Topic naming

kazmaps.<service>.<entity>
  • <service> — имя сервиса-publisher’а (user, review, media, …).
  • <entity> — доменная сущность в единственном числе (place, photo, account, building).
  • Действие (created, updated, deleted, banned, …) — НЕ в имени топика, а в заголовке Event-Type (см. Envelope).

Топик — это поток событий одной сущности, а не одного действия. Все изменения сущности (createdupdatedclosed) идут в один топик. Ключ записи = id сущности.

Канон — только kazmaps.<service>.<entity>. Никаких коротких имён без префикса и без <action> в имени топика.

Почему сущность, а не событие

Ключ записи = id сущности, поэтому все события одной сущности попадают в одну партицию и обрабатываются строго по порядку. Если разнести действия по разным топикам (...place.created, ...place.updated), межтопиковый порядок Kafka не гарантирует — денормализатор может применить updated раньше created. Per-entity-топик с ключом-сущностью это исключает — это требование корректности для stateful-consumer’ов (денормализация, инвалидация кэша, проекции), а их в платформе большинство.

Цена — нет подписки на отдельное действие на уровне топика; она почти не нужна и заменяется фильтром по Event-Type в consumer’е (диспетчер и так маршрутизирует по Event-Type).

Миграция legacy

Короткие имена (review.created, photo.uploaded, user.banned) и ранее-канонические per-action имена (kazmaps.<service>.<entity>.<action>) мигрируют на per-entity kazmaps.<service>.<entity>:

  1. Publisher начинает dual-publish — старый топик + per-entity, действие проставлено в Event-Type. TTL окна — 30 дней.
  2. Consumer’ы переподписываются на per-entity-топик и маршрутизируют по Event-Type (по одному PR на consumer).
  3. Когда все consumer’ы переехали, publisher останавливает публикацию в старые топики; их consumer-group’ы удаляются.
  4. Старые топики удаляются из кластера через ещё 30 дней (retention).

Новые топики заводятся только в формате kazmaps.<service>.<entity>; создать топик с <action> в имени — review-блокер.

Envelope

Каждое сообщение несёт metadata (Watermill message.Metadata). Это cross-service контракт — consumer любого сервиса может полагаться на эти поля.

ПолеЧто это
Event-TypeТип события (review.created).
Schema-VersionВерсия payload’а ("1").
Correlation-IdULID запроса, породившего событие.
Source-ServiceИмя publisher’а.
Published-AtRFC3339Nano.
traceparentW3C trace context.

Подробно — conventions/events.

Versioning

  • Non-breaking (добавили опциональное поле) → оставь ту же Schema-Version. Consumer’ы, ещё не знающие о новом поле, молча игнорируют его.
  • Breaking (удалили поле, сменили тип, переименовали) → два варианта:
    1. Bump Schema-Version в metadata и временно dual-publish в старую и новую схему, пока consumer’ы не переехали.
    2. Новый topic kazmaps.<service>.<entity>.v2 — для радикального редизайна payload’а. Старый топик живёт до остановки старых consumer’ов.

История Schema-Version конкретного топика — в docs/events.md репо publisher’а.

Retention

Класс событияTTL в топике
Business event (regular)7 дней
Sensitive (ban, role change)30 дней
DLQ90 дней

Retention настраивается на уровне Kafka cluster через topic config. Сервис не ретеншит сам.

DLQ naming

<topic>.dlq

Пример: kazmaps.review.review.dlq. Конфигурация Watermill PoisonQueue — в conventions/events.

Как добавлять новое событие

  1. Придумай имя топика по конвенции выше.
  2. Открой how-to/add-kafka-event и пройди по шагам: payload type, publisher, outbox, envelope.
  3. Добавь запись в таблицу ниже в том же PR, что и код события.
  4. Детальный payload опиши в docs/events.md репо сервиса-publisher’а, в таблице ниже ссылайся на docs/events.md#<topic> этого репо.
  5. Если есть consumer(ы) — пропиши их тоже; список consumer’ов обновляет тот, кто подписывается, в том же PR, которым заводит handler.

Топики

Детальный payload и Schema-Version history — в репо publisher’а. Таблица ниже — directory: topic | publisher | consumers | payload source.

user (publisher: user)

TopicPublisherConsumersPayload source
kazmaps.user.accountusernotificationdocs/events.md в репо user-service — Event-Type: account.registered/updated/banned/unbanned/level_changed
kazmaps.user.push_tokenusernotificationdocs/events.md в репо user-service — Event-Type: push_token.created/updated/deleted

review (publisher: review)

TopicPublisherConsumersPayload source
kazmaps.review.reviewreviewnotificationdocs/events.md в репо review-service — Event-Type: review.created/updated/deleted/replied

media (publisher: media)

TopicPublisherConsumersPayload source
kazmaps.media.photomediacatalogdocs/events.md в репо media-service — Event-Type: photo.uploaded/deleted/tagged
kazmaps.media.internal.processingmediamedia (internal pipeline)docs/events.md в репо media-service

Топик kazmaps.media.internal.processingвнутренний pipeline-топик сервиса media: handler загрузки кладёт задачу processing’а, worker того же сервиса её забирает. Это не межсервисное API, другие сервисы не подписываются.

catalog (publisher: catalog)

TopicPublisherConsumersPayload source
kazmaps.catalog.placecataloglistings, indoor, notificationdocs/events.md в репо catalog — Event-Type: place.created/updated/closed/verified/category_changed/needs_canonical/rating_updated
kazmaps.catalog.correctioncatalognotificationdocs/events.md в репо catalog — Event-Type: correction.approved/rejected

Consume-side DLQ — один консолидированный kazmaps.catalog.dlq (исходный топик в metadata).

listings (publisher: listings)

TopicPublisherConsumersPayload source
kazmaps.listings.providerlistingscatalogdocs/events.md в репо listings — Event-Type: provider.created/updated/deleted
kazmaps.listings.servicelistingscatalogdocs/events.md в репо listings — Event-Type: service.created/updated/deleted
kazmaps.listings.productlistingscatalogdocs/events.md в репо listings — Event-Type: product.created
kazmaps.listings.charging_stationlistingsdocs/events.md в репо listings — Event-Type: charging_station.created
kazmaps.listings.provider_servicelistingsdocs/events.md в репо listings — Event-Type: provider_service.linked/unlinked

indoor (publisher: indoor)

Топик на сущность: kazmaps.indoor.<entity>, Event-Type <entity>.created/updated/deleted.

TopicPublisherConsumersPayload source
kazmaps.indoor.buildingindoordocs/events.md в репо indoor
kazmaps.indoor.floor_planindoordocs/events.md в репо indoor
kazmaps.indoor.{wall,wall_adjacency,space,area,fixture,marker,vertical_connector,vc_endpoint}indoordocs/events.md в репо indoor

indoor consume: kazmaps.catalog.place (Event-Type place.closed), kazmaps.geo.building (Event-Type building.deleted).

moderation (publisher: внешний модерационный сервис)

Publisher живёт вне списка handbook-овских сервисов; consumer’ы потребляют по факту появления сообщений в топике.

TopicPublisherConsumersPayload source
kazmaps.moderation.photoвнешний модерационный сервисmediadocs/events.md в репо media-service (consumed-contract) — Event-Type: photo.approved/rejected
kazmaps.moderation.itemвнешний модерационный сервисnotificationdocs/events.md в репо notification-service (consumed-contract) — Event-Type: item.rejected

Для consumed-only контрактов ссылка указывает на репо одного из consumer’ов, где контракт зафиксирован со стороны consumer’а; при появлении publisher-репо эти записи переедут туда.

DLQ-топики

Все основные топики имеют пару <topic>.dlq. Сообщения попадают туда после исчерпания retry. Чтение DLQ — ручное, через psql / kafka-console-consumer, по инциденту. Retention 90 дней.

Примеры:

kazmaps.review.review.dlq kazmaps.media.photo.dlq kazmaps.catalog.dlq # consume-side, консолидированный kazmaps.indoor.building.dlq

Что не в каталоге

В этот каталог не входят:

  • Тестовые топики (*_test, *-local) — локальная разработка.
  • Топики внешних доменов, на которые notification уже подписан, но чьи publisher-сервисы ещё не переведены на канон / не существуют: kazmaps.booking.booking, kazmaps.queue.ticket, kazmaps.geo.correction, kazmaps.nav.correction, kazmaps.achievement.achievement, kazmaps.message.message, kazmaps.favorite.favorite, kazmaps.event.event, kazmaps.coupon.coupon, kazmaps.safety_report.report. Они попадут в каталог, когда соответствующие сервисы начнут публиковать реальные события; до этого notification просто подписан и ничего не получает.
  • Internal команды через HTTP — это REST, см. conventions/http-api.
Last updated on