Стандартизация событий микросервисов: Avro, Schema Registry и контракты

Микросервисы обмениваются сообщениями через брокер. Без чёткого контракта любое изменение в формате события ломает потребителей. Однажды переименование поля в событии `OrderShipped` привело к падению трёх сервисов в 3:00 ночи. Восстановление заняло 4 часа, а средний убыток от такого инцидента — $15

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Стандартизация событий микросервисов: Avro, Schema Registry и контракты
Сложный
~3-5 дней

Наши компетенции:

Часто задаваемые вопросы

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1419
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1287
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    983
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1244
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    983
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Разработка веб-сайта для компании ФИКСПЕР
    998

Микросервисы обмениваются сообщениями через брокер. Без чёткого контракта любое изменение в формате события ломает потребителей. Однажды переименование поля в событии OrderShipped привело к падению трёх сервисов в 3:00 ночи. Восстановление заняло 4 часа, а средний убыток от такого инцидента — $15 000. Event Schema — это контракт, который гарантирует, что все изменения явные и контролируемые. Он предотвращает 80% проблем совместимости ещё до деплоя. Свяжитесь с нами — мы поможем спроектировать схемы для вашей архитектуры и избавиться от ночных падений.

Avro с Schema Registry в 3 раза быстрее при сериализации, чем JSON без схемы, и обеспечивает строгую типизацию. Мы гарантируем обратную совместимость на всех этапах эволюции — так вы избежите ночных инцидентов и ускорите разработку.

Почему Avro, а не JSON?

Avro — бинарный формат сериализации, который компактнее JSON в 3-5 раз. Он строго типизирован: поле может быть только того типа, который описан в схеме. Schema Registry автоматически проверяет совместимость новой схемы со старыми: если вы добавили обязательное поле без default, регистр отклонит регистрацию. JSON не предоставляет таких гарантий, и ошибки совместимости обнаруживаются только в рантайме.

Характеристика Avro JSON (без схемы)
Типизация строгая динамическая
Размер сообщения компактный (бинарный) избыточный (текстовый)
Совместимость автоматическая (Schema Registry) ручная
Скорость сериализации высокая (до 3x быстрее) низкая
Поддержка эволюции встроенная (default, alias) отсутствует

Какие принципы лежат в основе Event Schema?

События описывают факты, не команды. OrderShipped — это факт. ShipOrder — это команда. Событие произошло и не может быть отменено (только компенсировано другим событием).

Схема должна быть самодостаточной. Консьюмер не должен делать дополнительных запросов для обработки события. Все нужные данные — в теле события.

Обратная совместимость по умолчанию. Старые консьюмеры должны работать с новыми событиями без изменений.

Структура события

{ "eventId": "01HQ2XK4VB8M9QXYZ123456789", "eventType": "order.shipped", "eventVersion": "1.2", "occurredAt": "2025-03-28T14:22:00.000Z", "producedBy": "order-service", "correlationId": "req-abc-123", "causationId": "cmd-xyz-456", "aggregateType": "Order", "aggregateId": "12345", "aggregateVersion": 7, "payload": { "orderId": 12345, "userId": 67890, "carrier": "DHL", "trackingCode": "JD123456789DE", "estimatedDelivery": "2025-03-31", "items": [ {"sku": "PROD-001", "quantity": 2, "warehouseId": "WH-MSK"} ] } } 
Поле конверта Описание
eventId ULID или UUID для идемпотентности
eventType Иерархический: domain.aggregate.action
eventVersion Semantic versioning схемы payload
occurredAt UTC ISO 8601
correlationId Для трассировки цепочки запросов
aggregateId + aggregateVersion Для оптимистичной блокировки

Avro-схема с эволюцией

{ "type": "record", "name": "OrderShipped", "namespace": "com.example.orders.events", "doc": "Событие отгрузки заказа со склада", "fields": [ {"name": "eventId", "type": "string"}, {"name": "eventType", "type": "string", "default": "order.shipped"}, {"name": "occurredAt", "type": {"type": "long", "logicalType": "timestamp-millis"}}, {"name": "orderId", "type": "long"}, {"name": "userId", "type": "long"}, {"name": "carrier", "type": "string"}, {"name": "trackingCode", "type": "string"}, { "name": "estimatedDelivery", "type": ["null", "string"], "default": null, "doc": "ISO date, может отсутствовать для некоторых перевозчиков" }, { "name": "warehouseId", "type": ["null", "string"], "default": null, "doc": "Добавлено в v1.1 — необязательное поле для backward compatibility" }, { "name": "shippingCost", "type": ["null", {"type": "bytes", "logicalType": "decimal", "precision": 10, "scale": 2}], "default": null, "doc": "Добавлено в v1.2" } ] } 

Правила эволюции для backward compatibility:

  • Новые поля — всегда с default (null или значение)
  • Нельзя удалять обязательные поля
  • Нельзя менять тип поля
  • Нельзя переименовывать поля (добавьте alias, потом через мажорную версию переименуйте)

Чтобы добавить поле warehouseId без нарушения совместимости, укажите "default": null и тип ["null", "string"]. Тогда старые консьюмеры, не знающие об этом поле, просто получат null.

Как тестировать совместимость схем?

Мы автоматически проверяем обратную совместимость в CI/CD: сериализуем событие на стороне продюсера, десериализуем на стороне консьюмера и убеждаемся, что старые версии не падают. Это позволяет ловить breaking changes до деплоя. Контрактное тестирование фиксирует ожидания обеих сторон. Только один инцидент из-за несовместимости схем обходится в среднем в $15 000 при ночном деплое.

Как обеспечить обратную совместимость?

# Настройка Schema Registry — BACKWARD совместимость для всех событий orders curl -X PUT http://schema-registry:8081/config/order-events-value \ -H "Content-Type: application/vnd.schemaregistry.v1+json" \ -d '{"compatibility": "BACKWARD_TRANSITIVE"}' # BACKWARD_TRANSITIVE — новая схема совместима со ВСЕМИ предыдущими версиями, # не только с последней 

Мажорное изменение (breaking change) — новый топик:

  • order-events-v1 → для консьюмеров на старой схеме
  • order-events-v2 → новая схема, консьюмеры мигрируют постепенно

Переходный период: продюсер публикует в оба топика. После полной миграции — order-events-v1 deprecated.

Event Catalog — документирование схем

Для команды из нескольких сервисов критично иметь центральный реестр событий. Используем AsyncAPI для описания каналов и сообщений.

asyncapi: 3.0.0 info: title: Order Service Events version: 1.0.0 description: События, публикуемые Order Service channels: order-events: address: order-events messages: OrderCreated: $ref: '#/components/messages/OrderCreated' OrderShipped: $ref: '#/components/messages/OrderShipped' OrderCancelled: $ref: '#/components/messages/OrderCancelled' components: messages: OrderCreated: name: OrderCreated title: Заказ создан summary: Публикуется при успешном создании нового заказа contentType: application/avro headers: type: object properties: correlationId: type: string description: ID входящего HTTP-запроса payload: type: object required: [eventId, orderId, userId, items, totalAmount] properties: eventId: type: string format: ulid orderId: type: integer format: int64 userId: type: integer format: int64 items: type: array items: type: object properties: sku: type: string quantity: type: integer price: type: number totalAmount: type: number createdAt: type: string format: date-time 

Типизированный Event Publisher (TypeScript/Node.js)

import { SchemaRegistry } from '@kafkajs/confluent-schema-registry'; import { Kafka } from 'kafkajs'; interface EventEnvelope<T> { eventId: string; eventType: string; eventVersion: string; occurredAt: string; producedBy: string; correlationId?: string; aggregateType: string; aggregateId: string; aggregateVersion: number; payload: T; } interface OrderShippedPayload { orderId: number; userId: number; carrier: string; trackingCode: string; estimatedDelivery?: string; } class OrderEventPublisher { private registry: SchemaRegistry; private producer: ReturnType<Kafka['producer']>; async publishOrderShipped(data: OrderShippedPayload, correlationId?: string): Promise<void> { const envelope: EventEnvelope<OrderShippedPayload> = { eventId: ulid(), eventType: 'order.shipped', eventVersion: '1.2', occurredAt: new Date().toISOString(), producedBy: 'order-service', correlationId, aggregateType: 'Order', aggregateId: String(data.orderId), aggregateVersion: await this.getAggregateVersion(data.orderId), payload: data, }; const schemaId = await this.registry.getLatestSchemaId('order-events-value'); const encoded = await this.registry.encode(schemaId, envelope); await this.producer.send({ topic: 'order-events', messages: [{ key: String(data.orderId), value: encoded, headers: { 'correlation-id': correlationId ?? '', 'event-type': 'order.shipped', }, }], }); } } 

Как проходит внедрение Event Schema?

В первый день проводим воркшоп с командами сервисов: составляем Event Storming карту, определяем все доменные события и их границы. На второй день разрабатываем Avro-схемы для каждого типа события, фиксируем правила именования и структуру конверта. Регистрируем их в Schema Registry. Третий день — реализация типизированных Event Publisher'ов в каждом сервисе-продюсере и создание AsyncAPI-документации. Четвёртый день — контрактные тесты, интеграция проверки совместимости в CI/CD и инструкция для команды по правилам эволюции схем.

Что входит в разработку схемы событий под ключ

  • Event Storming воркшоп и документирование всех событий
  • Avro-схемы с обратной совместимостью и версионированием
  • Настройка Schema Registry (Kafka) с правилами BACKWARD_TRANSITIVE
  • Типизированные Event Publisher'ы на TypeScript/Node.js или Java/Scala
  • Библиотека для сериализации и десериализации событий
  • AsyncAPI-спецификация для центрального реестра событий
  • Контрактные тесты, интегрированные в CI/CD
  • Документация для команды и обучение разработчиков
  • Сопровождение на этапе миграции старых консьюмеров

Мы гарантируем качество результата: наш опыт — 7 лет в проектировании реактивных систем и микроядерной архитектуры, выполнено более 30 проектов по внедрению событийно-ориентированных интеграций. Получите консультацию по проектированию схем событий — мы оценим ваш проект и предложим оптимальное решение.