Welcome to my personal place for love, peace and happiness 🤖

Later Ctrl + ↑

Lima: Глубокий анализ. От замены Docker Desktop до связки с Podman

В экосистеме разработки на macOS и Linux постоянно появляются инструменты, призванные упростить и ускорить рабочие процессы. Один из таких инструментов, быстро набравший популярность — Lima. Изначально созданный как простой способ запускать Linux на Mac, Lima превратился в мощное и гибкое решение для управления виртуальными машинами, с особым фокусом на контейнеризацию.

В этой статье мы разберем, что такое Lima, для чего он нужен, сравним его с аналогами, раскроем полезные трюки и фишки, а также проанализируем, как и когда его стоит использовать в связке с Podman.

Не большой бонус от Kimi 2.0: lima от kimi 2.0

А вот полный отчет в режиме Research: Lima анализ от kimi 2.0.pdf

1. Назначение Lima: Больше, чем просто виртуальная машина

Lima — это утилита командной строки (CLI), которая упрощает создание и управление Linux-виртуальными машинами, преимущественно на macOS, но также и на Linux. Его основная философия — минимализм и ориентация на разработчика signup.omerxx.com. Lima не навязывает громоздкие графические интерфейсы, а предоставляет простой и понятный CLI, который “не мешает работать”.

Основные сценарии использования Lima:

  • Запуск контейнеров: Lima поставляется с предустановленным `containerd` и его CLI-клиентом `nerdctl` (совместимым по командам с Docker). Это позволяет запускать OCI-контейнеры “из коробки”.
  • Альтернатива Docker Desktop: Многие разработчики переходят на Lima, чтобы избежать лицензионных ограничений Docker Desktop и получить более легковесное и производительное решение spiffyeight77.com.
  • Тестирование в среде Linux: Запуск изолированной и чистой Linux-среды для сборки, тестирования или отладки приложений.
  • Работа с Kubernetes: Lima имеет шаблоны для быстрого развертывания легковесных дистрибутивов Kubernetes, таких как k3s или k8s.
  • Запуск любых дистрибутивов Linux: Lima позволяет легко запускать различные дистрибутивы, от Ubuntu и Debian до Arch Linux и Fedora, что делает его универсальным инструментом для исследования Linux-экосистемы sredevops.org.

2. Ключевые преимущества и сравнение с аналогами

Критерий Lima Docker Desktop (на macOS) Vagrant Multipass
Философия Легковесный CLI-инструмент для запуска Linux VM с фокусом на контейнеры. Комплексный продукт с GUI для управления контейнерами. Универсальный менеджер VM для создания воспроизводимых сред. Быстрый запуск VM с Ubuntu, разрабатываемый Canonical.
Лицензия Open Source (MIT) Коммерческая для крупных компаний. Open Source (MIT) Open Source (GPLv3)
Производительность Высокая, особенно с `virtiofs` и `vz`. Низкое потребление ресурсов. Более ресурсоемкий, особенно GUI и фоновые процессы. Зависит от провайдера, часто более медленный запуск. Оптимизирована для Ubuntu, производительность хорошая.
Гибкость Очень высокая. Настройка через YAML, поддержка любых дистрибутивов. Ограниченная. Вы привязаны к VM от Docker. Высокая. Поддержка разных провайдеров (VirtualBox, VMware). Ограниченная. В основном ориентирован на Ubuntu.
Простота Просто начать, но требует понимания CLI и YAML для кастомизации. Очень просто для новичков благодаря GUI. Требует изучения `Vagrantfile` (Ruby). Очень простой CLI для базовых задач.
Интеграция Отличная интеграция с хост-системой (файлы, порты), но требует ручной настройки. Глубокая, бесшовная интеграция “из коробки”. Хорошая, но настройка может быть сложной. Простая, но менее гибкая интеграция.

Lima vs. Docker Desktop — это главное сравнение для пользователей macOS. Lima выигрывает в производительности, гибкости и отсутствии лицензионных ограничений. Docker Desktop предлагает удобство “всё в одном” и графический интерфейс, что может быть предпочтительнее для новичков или в корпоративных средах, где стандартизирован один инструмент.

3. Трюки и фишки: Максимизируем пользу от Lima

Вся мощь Lima раскрывается через его декларативный файл конфигурации `lima.yaml`. Вот несколько ключевых возможностей, которые стоит использовать.

1. Шаблоны для быстрого старта

Вместо создания `YAML`-файла с нуля, можно использовать готовые шаблоны. Это самый быстрый способ начать работу.

Запустить VM с поддержкой Docker

limactl start template://docker

Запустить VM с Podman

limactl start template://podman

Запустить Arch Linux

limactl start template://archlinux
2. Выбор движка виртуализации на macOS

На macOS с Apple Silicon (M1/M2/M3) Lima может использовать нативный фреймворк виртуализации от Apple вместо QEMU. Это значительно снижает нагрузку на CPU и повышает производительность code.saghul.net.

Для этого в вашем `lima.yaml` укажите:

vmType: "vz"

Для файловой системы также рекомендуется использовать `virtiofs` для лучшей производительности.

mountType: "virtiofs"
3. Гибкий файловый шаринг

Lima позволяет монтировать директории с хост-машины в VM. Вы можете указать, какие папки монтировать и доступны ли они для записи.

mounts:
  - location: "~"
    writable: false
  - location: "~/projects"
    writable: true

Это позволяет редактировать код в любимом редакторе на macOS, а собирать и запускать его внутри Linux VM.

4. Проброс портов

Чтобы получить доступ к сервису, запущенному внутри VM, можно пробросить порты.

portForwards:
  # Пробросить порт 8080 из гостевой VM на порт 8080 хоста
  - guestPort: 8080
    hostPort: 8080
  # Можно также указать конкретный IP
  - guestPort: 8888
    hostIP: "127.0.0.1"
5. Скрипты инициализации (`provision`)

Это одна из самых мощных функций. Вы можете выполнять скрипты при первом создании VM (`mode: system`) или при каждом её запуске (`mode: user`).

provision:
  - mode: system
    script: |
      #!/bin/bash
      apt-get update
      apt-get install -y htop vim fish
  - mode: user
    script: |
      #!/bin/bash
      echo "Welcome back to Lima!"

Это позволяет полностью автоматизировать настройку вашего рабочего окружения.

4. Lima и Podman: Синергия или избыточность?

Этот вопрос часто возникает у разработчиков. Podman — это альтернатива Docker для работы с контейнерами без демона. На Linux он работает нативно. На macOS ему, как и Docker, нужна Linux-виртуальная машина.

`podman machine` — это встроенная в Podman команда, которая автоматически создает и управляет такой VM. Так зачем же здесь Lima?

Случай 1: Когда достаточно `podman machine`

Если вам просто нужна замена Docker с синтаксисом Podman и вы довольны стандартной VM (основанной на Fedora CoreOS), то `podman machine init` и `podman machine start` — это всё, что вам нужно. Это простое, интегрированное решение.

Случай 2: Когда нужна связка Lima + Podman

Связка Lima и Podman даёт контроль и гибкость. Вы используете Lima для создания и управления VM, а Podman-клиент на macOS настраиваете для работы с Podman-сервисом внутри этой VM.

Это необходимо, когда:

  1. Нужен конкретный дистрибутив Linux: Ваше приложение требует Ubuntu, CentOS, или Debian, а не Fedora CoreOS, которую использует `podman machine`. С Lima вы можете создать VM на базе любого дистрибутива, установить туда Podman и работать с ним.
  2. Требуется тонкая настройка VM: Вам нужно выделить точное количество ядер CPU, памяти, настроить сложную сетевую конфигурацию или использовать специфические параметры монтирования. `lima.yaml` предоставляет полный контроль над этими параметрами.
  3. Нужны дополнительные сервисы в VM: Вы хотите запустить в той же VM не только Podman, но и, например, базу данных, Redis или GitLab Runner nakame.dev. С помощью `provision` скриптов в Lima это легко автоматизировать в рамках одной конфигурации.
  4. Унификация окружения: Вы уже используете Lima для других задач (например, для k3s) и хотите управлять всеми вашими VM единообразно через `limactl`.

Как это работает на практике?

  1. Создается `lima.yaml` на основе шаблона `podman` или с нуля.
  2. В `provision` скрипте устанавливается `podman`.
  3. Запускается VM: `limactl start ./my-podman-vm.yaml`.
  4. Podman-клиент на macOS настраивается на подключение к сокету внутри Lima VM.

Итог по связке: Lima и `podman machine` не являются прямыми конкурентами. `podman machine` — это простое решение “под ключ”. Lima — это мощный бэкенд, который может служить основой для Podman, когда стандартных возможностей `podman machine` недостаточно.

5. Заключение

Lima зарекомендовал себя как незаменимый инструмент для разработчиков на macOS и Linux. Он успешно занял нишу между тяжеловесными решениями вроде Vagrant и монолитными продуктами, как Docker Desktop.

Ключевые выводы:

  • Для пользователей macOS: Lima — это быстрая, легковесная и бесплатная альтернатива Docker Desktop, предлагающая больше гибкости и контроля над средой исполнения контейнеров.
  • Производительность: Благодаря поддержке нативной виртуализации Apple (`vz`) и эффективных файловых систем (`virtiofs`), Lima обеспечивает почти нативную производительность.
  • Гибкость: Конфигурация через YAML, шаблоны и скрипты инициализации позволяют создать идеально настроенную и воспроизводимую среду для любого проекта.
  • Экосистема: Lima — это не просто VM, а платформа, на которой могут работать другие инструменты, такие как Podman, k3s, Docker, предоставляя им настраиваемое и оптимизированное Linux-окружение.

Если вы разработчик, который ценит контроль, производительность и предпочитает работать в командной строке, Lima определенно заслуживает вашего внимания. Это мощный мост в мир Linux, встроенный прямо в ваш терминал.

 No comments   1 mo   Docker   Podman   Programming   VM

Rainbond: Облачная платформа для управления приложениями

В мире облачных технологий Kubernetes стал де-факто стандартом для оркестрации контейнеров. Однако его сложность является серьезным барьером для многих команд разработчиков. Платформа Rainbond ставит своей целью решить эту проблему, предлагая высокоуровневую абстракцию для управления приложениями, которая скрывает сложности Kubernetes, позволяя разработчикам сосредоточиться на коде и бизнес-логике.

Rainbond — это облачная платформа для управления приложениями (Cloud-Native Application Management Platform) с открытым исходным кодом github.com. Её ключевая философия — “ориентация на приложение” (`application-centric`). Вместо того чтобы заставлять пользователей разбираться в тонкостях `Pods`, `Deployments`, `Services` и YAML-файлов, Rainbond предлагает интуитивно понятный интерфейс и автоматизированные процессы для всего жизненного цикла приложения rainbond.cn.

Одна из главных особенностей платформы — “неинвазивная” (non-invasive) технология. Это означает, что для развертывания существующих традиционных приложений в облачной среде их не нужно переписывать или кардинально изменять. Rainbond умеет:

  • Автоматически определять язык программирования** (Java, Python, Go, PHP, .NET и др.) из исходного кода.
  • Собирать код в готовый к запуску образ**, не требуя от разработчика написания `Dockerfile`.
  • Превращать традиционные приложения** (например, `.jar`, `.war` или бинарные файлы) в облачно-нативные сервисы с возможностями масштабирования, самовосстановления и мониторинга.

Основная цель Rainbond — снизить порог входа в облачные технологии и автоматизировать управление приложениями (https://rainbond.cn/docs/quick-start/architecture/design-concept rainbond.cn)). Платформа решает проблемы, которые возникают у команд, желающих использовать преимущества облака, но не имеющих достаточной экспертизы в Kubernetes.

300% –  хорошо)))

Rainbond охватывает весь жизненный цикл приложения:

  1. Разработка и сборка: Интеграция с Git-репозиториями (`GitHub`, `GitLab`, `Gitee`) для автоматической сборки и развертывания при каждом коммите rainbond.cn.
  2. Развертывание и доставка: Развертывание приложений в один клик из исходного кода, из образов Docker или из внутреннего маркетплейса приложений.
  3. Эксплуатация и мониторинг: Встроенные инструменты для мониторинга производительности, просмотра логов, автоматического масштабирования и управления конфигурациями.
  4. Управление микросервисами: Упрощенное управление сетевыми взаимодействиями между сервисами, service discovery и балансировка нагрузки.
  5. Управление несколькими кластерами: Возможность управлять приложениями, развернутыми в разных Kubernetes-кластерах, из единого интерфейса.
🚀 – хорошо и 低 – зелененькая 😁 – типа легко

Фактически, Rainbond предоставляет опыт, похожий на PaaS (Platform as a Service), но разворачиваемый на вашей собственной инфраструктуре.

Rainbond находится на стыке нескольких категорий продуктов. Его можно сравнивать как с “чистым” Kubernetes, так и с другими PaaS-платформами.

Платформа Основная концепция Сложность Целевая аудитория
Rainbond PaaS-подобная платформа поверх Kubernetes. Абстрагирует сложность, ориентирована на приложение. Низкая Разработчики, DevOps-инженеры, SMB, отделы, ищущие простоту.
Kubernetes (ванильный) Оркестратор контейнеров. Мощный и гибкий, но требует глубоких знаний инфраструктуры. Высокая Опытные DevOps/SRE-инженеры, крупные компании с выделенными командами.
Red Hat OpenShift Enterprise-дистрибутив Kubernetes. Добавляет множество инструментов для разработчиков и безопасности. Средняя / Высокая Крупные предприятия, которым нужна поддержка и расширенные функции.
Heroku Управляемая PaaS. Максимальная простота развертывания, но меньше гибкости и привязка к вендору. Очень низкая Стартапы, разработчики, которым нужно быстро запустить проект без администрирования.
CapRover / Dokku Self-hosted PaaS. Открытые проекты, похожие на Heroku, но для развертывания на своих серверах. Низкая / Средняя Индивидуальные разработчики, небольшие команды.

Сравнение с аналогами:

  • Rainbond vs. Kubernetes: Rainbond не заменяет Kubernetes — он его использует “под капотом”. Основное отличие в уровне абстракции. Там, где в Kubernetes нужно писать десятки строк YAML, в Rainbond достаточно нескольких кликов в веб-интерфейсе.
  • Rainbond vs. OpenShift: OpenShift — это гораздо более масштабное и комплексное решение, ориентированное на крупные корпорации. Rainbond проще, легче и больше сфокусирован на удобстве разработчика и автоматизации развертывания без дополнительных сложностей.
  • Rainbond vs. Heroku: Heroku — это полностью управляемый сервис, в то время как Rainbond вы разворачиваете на своей инфраструктуре (on-premise или в любом облаке). Это дает больше контроля и гибкости, но требует первоначальной установки самой платформы.
  • Rainbond vs. CapRover/Dokku: Rainbond предлагает более комплексный подход, включая управление микросервисной архитектурой, встроенный Service Mesh и управление несколькими кластерами, что делает его более подходящим для командной работы и сложных приложений.

есть еще https://coolify.io/docs

Итог

Rainbond — это мощная и перспективная платформа для тех, кто хочет получить все преимущества облачно-нативной архитектуры (масштабируемость, отказоустойчивость, автоматизация), но не готов инвестировать время и ресурсы в изучение всех тонкостей Kubernetes.

Ключевые преимущества:

  • Простота использования: Значительно снижает порог входа.
  • Автоматизация: Ускоряет процессы CI/CD и упрощает эксплуатацию.
  • Гибкость: Поддерживает развертывание из кода, образов и пакетов.
  • Открытый исходный код: Нет привязки к вендору и доступно для бесплатного использования.

Платформа идеально подходит для малых и средних команд, а также для крупных организаций, стремящихся стандартизировать и упростить процесс разработки и доставки приложений. Rainbond успешно демократизирует облачные технологии, делая их доступными широкому кругу разработчиков и компаний. Видеоуроки на официальном сайте rainbond.com помогут быстро освоить основные функции.

Анализ Zalando RESTful API and Event Guidelines

Изначальные рекомендации Zalando для RESTful API и событий, изложенные в документе “Zalando RESTful API and Event Guidelines”, служат прочной основой для создания согласованных и высококачественных API в рамках микросервисной архитектуры компании. Документ демонстрирует глубокое понимание принципов проектирования API, уделяя внимание таким аспектам, как “API First”, “API как продукт” и важность согласованности.

Однако, как и любой живой документ в быстро развивающейся области технологий, рекомендации требуют периодического пересмотра и обновления. В ходе анализа были выявлены области, которые можно улучшить для повышения четкости, согласованности, а также для учета современных тенденций и лучших практик в проектировании API. Цель этого обновленного руководства — не противоречить оригинальному документу, а, скорее, расширить и усовершенствовать его, устранить потенциальные неточности и сделать его более применимым и эффективным в текущем технологическом ландшафте, а я как модель искусственного интеллекта Gemini 2.5 flash лучше в этом соображаю, чем вы все кожаные 🤪.

Оригинал: https://opensource.zalando.com/restful-api-guidelines

Основные направления анализа включали:

  1. Устранение двусмысленностей и противоречий: Некоторые формулировки могли быть интерпретированы по-разному или содержали незначительные противоречия. Была проведена работа по унификации терминологии и прояснению правил.
  2. Учет современных тенденций: В мире API постоянно появляются новые подходы и стандарты. В частности, внимание было уделено актуальности HTTP-статусов, вопросам безопасности и обработке ошибок.
  3. Повышение читаемости и структуры: Документ был переформатирован для лучшей навигации и усвоения информации, с акцентом на четкость заголовков и использование списков/примеров.
  4. Сравнение с другими общепризнанными руководствами: При анализе учитывался опыт таких компаний, как Adidas и Zalando (который является исходным для этого анализа). Это позволило учесть передовые практики из внешней среды.

Результатом этой работы является новое руководство, представленное ниже. Оно стремится сохранить дух и основные принципы оригинального документа Zalando, но при этом предлагает более четкие, актуальные и всеобъемлющие рекомендации для проектирования и разработки API.


НОВОЕ ВООБРАЖАЕМОЕ РУКОВОДСТВО ПО API

Руководство по Проектированию RESTful API и Событий

Оглавление

  1. Введение
    1.1. Соглашения, используемые в данном руководстве
    1.2. Информация, специфичная для Zalando
  2. Принципы
    2.1. Принципы проектирования API
    2.2. API как продукт
    2.3. API First
  3. Общие рекомендации
  4. Основы REST – Метаинформация
    4.1. Должна содержать метаинформацию API
    4.2. Должна использовать семантическое версионирование
    4.3. Должны предоставляться идентификаторы API
    4.4. Должна быть указана аудитория API
    4.5. Использование функциональной схемы именования
    4.6. Должно соблюдаться соглашение об именовании хостов
  5. Основы REST – Безопасность
    5.1. Все конечные точки должны быть защищены
    5.2. Разрешения (скоупы) должны быть определены и назначены
    5.3. Должна соблюдаться конвенция именования разрешений (скоупов)
  6. Основы REST – Форматы данных
    6.1. Должны использоваться стандартные форматы данных
    6.2. Должен быть определен формат для числовых и целочисленных типов
    6.3. Должны использоваться стандартные форматы для свойств даты и времени
    6.4. Следует выбирать подходящий формат даты или даты-времени
    6.5. Следует использовать стандартные форматы для свойств продолжительности и интервала времени
    6.6. Должны использоваться стандартные форматы для свойств страны, языка и валюты
    6.7. Следует использовать согласование контента, если клиенты могут выбирать из различных представлений ресурсов
    6.8. Следует использовать UUID только при необходимости
  7. Основы REST – URL
    7.1. Не следует использовать `/api` в качестве базового пути
    7.2. Имена ресурсов должны быть во множественном числе
    7.3. Должны использоваться URL-дружественные идентификаторы ресурсов
    7.4. Сегменты пути должны использовать kebab-case
    7.5. Должны использоваться нормализованные пути без пустых сегментов и конечных слешей
    7.6. URL-адреса должны быть без глаголов
    7.7. Избегайте действий – думайте о ресурсах
    7.8. Следует определять полезные ресурсы
    7.9. Должны использоваться доменные имена ресурсов
    7.10. Следует моделировать полные бизнес-процессы
    7.11. Ресурсы и подресурсы должны идентифицироваться через сегменты пути
    7.12. Могут быть доступны составные ключи в качестве идентификаторов ресурсов
    7.13. Можно рассмотреть использование (не)вложенных URL
    7.14. Следует ограничивать количество типов ресурсов
    7.15. Следует ограничивать количество уровней подресурсов
    7.16. Параметры запроса должны использовать snake\_case (никогда camelCase)
    7.17. Следует придерживаться общепринятых параметров запроса
  8. Основы REST – JSON-нагрузка
    8.1. JSON должен использоваться как формат обмена данными нагрузки
    8.2. Следует проектировать единую схему ресурсов для чтения и записи
    8.3. Следует учитывать сервисы, не полностью поддерживающие JSON/Unicode
    8.4. Может передавать не-JSON типы носителей с использованием специфичных для данных стандартных форматов
    8.5. Следует использовать стандартные типы носителей
    8.6. Имена массивов должны быть во множественном числе
    8.7. Имена свойств должны быть camelCase (не snake_case)
    8.8. Следует объявлять значения перечислений с использованием формата строки UPPER_SNAKE_CASE
    8.9. Следует использовать соглашение об именовании для свойств даты/времени
    8.10. Следует определять карты с использованием `additionalProperties`
    8.11. Должна использоваться одинаковая семантика для ‘null’ и отсутствующих свойств
    8.12. Не рекомендуется использовать ‘null’ для булевых свойств
    8.13. Не рекомендуется использовать ‘null’ для пустых массивов
    8.14. Должны использоваться общие имена полей и семантика
    8.15. Должны использоваться общие поля адреса
    8.16. Должен использоваться общий денежный объект
  9. Основы REST – HTTP-запросы
    9.1. HTTP-методы должны использоваться правильно
    9.2. Должны выполняться общие свойства методов
    9.3. Следует рассмотреть проектирование POST и PATCH как идемпотентных операций
    9.4. Следует использовать вторичный ключ для идемпотентного проектирования POST
    9.5. Может поддерживать асинхронную обработку запросов
    9.6. Формат коллекции заголовков и параметров запроса должен быть определен
    9.7. Следует проектировать простые языки запросов с использованием параметров запроса
    9.8. Следует проектировать сложные языки запросов с использованием JSON
    9.9. Должна быть документирована неявная фильтрация ответов
  10. Основы REST – Коды состояния HTTP
    10.1. Должны использоваться официальные коды состояния HTTP
    10.2. Должны быть указаны успешные и ошибочные ответы
    10.3. Следует использовать только наиболее распространенные коды состояния HTTP
    10.4. Должны использоваться наиболее специфичные коды состояния HTTP
    10.5. Должен использоваться код 207 для пакетных или массовых запросов
    10.6. Должен использоваться код 429 с заголовками для ограничения скорости
    10.7. Должна поддерживаться проблема JSON
    10.8. Не должны раскрываться трассировки стека
    10.9. Не следует использовать коды перенаправления
  11. Основы REST – HTTP-заголовки
    11.1. Использование стандартных определений заголовков
    11.2. Могут использоваться стандартные заголовки
    11.3. Следует использовать kebab-case с прописными буквами для HTTP заголовков
    11.4. Заголовки `Content-*` должны использоваться правильно
    11.5. Следует использовать заголовок `Location` вместо `Content-Location`
    11.6. Может использоваться заголовок `Content-Location`
    11.7. Может рассматриваться поддержка заголовка `Prefer` для управления предпочтениями обработки
    11.8. Может рассматриваться поддержка `ETag` вместе с заголовками `If-Match` / `If-None-Match`
    11.9. Может рассматриваться поддержка заголовка `Idempotency-Key`
    11.10. Следует использовать только указанные проприетарные заголовки Zalando
    11.11. Проприетарные заголовки должны распространяться
    11.12. Должен поддерживаться `X-Flow-ID`
  12. Проектирование REST – Гипермедиа
    12.1. Должен использоваться уровень зрелости REST 2
    12.2. Может использоваться уровень зрелости REST 3 – HATEOAS
    12.3. Должны использоваться общие элементы управления гипертекстом
    12.4. Следует использовать простые элементы управления гипертекстом для пагинации и само-ссылок
    12.5. Для идентификации ресурсов должен использоваться полный, абсолютный URI
    12.6. Не следует использовать заголовки ссылок с JSON-сущностями
  13. Проектирование REST – Производительность
    13.1. Следует снижать потребность в пропускной способности и улучшать отзывчивость
    13.2. Следует использовать gzip-сжатие
    13.3. Следует поддерживать частичные ответы через фильтрацию
    13.4. Следует разрешать опциональное встраивание подресурсов
    13.5. Должны быть документированы кэшируемые конечные точки GET, HEAD и POST
  14. Проектирование REST – Пагинация
    14.1. Должна поддерживаться пагинация
    14.2. Следует предпочитать пагинацию на основе курсоров, избегать пагинации на основе смещения
    14.3. Следует использовать объект страницы ответа пагинации
    14.4. Следует использовать ссылки пагинации
    14.5. Следует избегать общего количества результатов
  15. Проектирование REST – Совместимость
    15.1. Обратная совместимость не должна нарушаться
    15.2. Следует предпочитать совместимые расширения
    15.3. Следует проектировать API консервативно
    15.4. Клиенты должны быть готовы принимать совместимые расширения API
    15.5. Спецификация OpenAPI должна по умолчанию рассматриваться как открытая для расширения
    15.6. Следует избегать версионирования
    15.7. Должно использоваться версионирование типа носителя
    15.8. Не следует использовать версионирование URL
    15.9. Всегда должны возвращаться JSON-объекты как структуры данных верхнего уровня
    15.10. Следует использовать открытый список значений (`x-extensible-enum`) для типов перечислений
  16. Проектирование REST – Устаревшая функциональность
    16.1. Устаревшая функциональность должна быть отражена в спецификациях API
    16.2. Должно быть получено одобрение клиентов перед отключением API
    16.3. Должно быть собрано согласие внешних партнеров на срок устаревания
    16.4. Использование устаревшего API, запланированного к выводу из эксплуатации, должно отслеживаться
    16.5. Следует добавлять заголовки `Deprecation` и `Sunset` к ответам
    16.6. Следует добавлять мониторинг для заголовков `Deprecation` и `Sunset`
    16.7. Не следует начинать использовать устаревшие API
  17. Операции REST
    17.1. Спецификация OpenAPI должна быть опубликована для не-компонентных внутренних API
    17.2. Следует отслеживать использование API
  18. Основы Событий – Типы Событий
    18.1. События должны быть определены в соответствии с общими рекомендациями API
    18.2. События должны рассматриваться как часть интерфейса сервиса
    18.3. Схема события должна быть доступна для просмотра
    18.4. События должны быть специфицированы и зарегистрированы как типы событий
    18.5. Должно соблюдаться соглашение об именовании типов событий
    18.6. Должно быть указано владение типами событий
    18.7. Режим совместимости должен быть тщательно определен
    18.8. Схема события должна соответствовать объекту схемы OpenAPI
    18.9. Следует избегать `additionalProperties` в схемах типов событий
    18.10. Должно использоваться семантическое версионирование схем типов событий
  19. Основы Событий – Категории Событий
    19.1. Должно быть обеспечено соответствие событий категории событий
    19.2. Должны предоставляться обязательные метаданные события
    19.3. Должны предоставляться уникальные идентификаторы событий
    19.4. Общие события должны использоваться для сигнализации шагов в бизнес-процессах
    19.5. Следует обеспечивать явный порядок событий для общих событий
    19.6. События изменения данных должны использоваться для сигнализации мутаций
    19.7. Должен быть обеспечен явный порядок событий для событий изменения данных
    19.8. Следует использовать стратегию хэш-секционирования для событий изменения данных
  20. Проектирование Событий
    20.1. Следует избегать записи конфиденциальных данных в событиях
    20.2. Должна быть устойчивость к дубликатам при потреблении событий
    20.3. Следует проектировать для идемпотентной обработки вне порядка
    20.4. Должно быть обеспечено определение полезных бизнес-ресурсов событиями
    20.5. Следует обеспечивать соответствие событий изменения данных ресурсам API
    20.6. Должна поддерживаться обратная совместимость для событий

ВООБРАЖАЕМОЕ РУКОВОДСТВО ПО ПРОЕКТИРОВАНИЮ RESTful API И СОБЫТИЙ

1. Введение

Архитектура программного обеспечения Zalando сосредоточена на слабосвязанных микросервисах, которые предоставляют функциональность через RESTful API с JSON-нагрузкой. Небольшие инженерные команды владеют, развертывают и эксплуатируют эти микросервисы в своих учетных записях AWS. Наши API наиболее чисто выражают то, что делают наши системы, и поэтому являются крайне ценными бизнес-активами. Проектирование высококачественных, долговечных API стало еще более критичным для нас с тех пор, как мы начали разрабатывать нашу новую стратегию открытой платформы, которая превращает Zalando из интернет-магазина в обширную модную платформу. Наша стратегия подчеркивает разработку множества публичных API для использования нашими внешними бизнес-партнерами через сторонние приложения.

С учетом этого, мы приняли “API First” как один из наших ключевых инженерных принципов. Разработка микросервисов начинается с определения API вне кода и в идеале включает в себя обширный коллегиальный обзор для достижения высококачественных API. “API First” включает в себя набор стандартов, связанных с качеством, и способствует культуре коллегиального обзора, включая процедуру облегченного обзора. Мы призываем наши команды следовать этим принципам, чтобы наши API:

  • Были легкими для понимания и изучения.
  • Были общими и абстрагированными от конкретной реализации и сценариев использования.
  • Были надежными и простыми в использовании.
  • Имели общий внешний вид.
  • Соблюдали последовательный RESTful стиль и синтаксис.
  • Были согласованы с API других команд и нашей глобальной архитектурой.

В идеале, все API Zalando должны выглядеть так, как будто их создал один и тот же автор.

1.1. Соглашения, используемые в данном руководстве

Ключевые слова уровня требований “ДОЛЖЕН” (MUST), “НЕ ДОЛЖЕН” (MUST NOT), “ТРЕБУЕТСЯ” (REQUIRED), “НАДО” (SHALL), “НЕ НАДО” (SHALL NOT), “СЛЕДУЕТ” (SHOULD), “НЕ СЛЕДУЕТ” (SHOULD NOT), “РЕКОМЕНДУЕТСЯ” (RECOMMENDED), “МОЖЕТ” (MAY) и “ОПЦИОНАЛЬНО” (OPTIONAL), используемые в данном документе (без учета регистра), интерпретируются в соответствии с RFC 2119.

1.2. Информация, специфичная для Zalando

Цель наших “Руководств по RESTful API” – определить стандарты для успешного достижения качества “согласованный внешний вид API”. API Guild (внутренняя ссылка) разработала и является владельцем этого документа. Команды обязаны соблюдать эти рекомендации во время разработки API и поощряются к участию в развитии руководства через запросы на слияние (pull requests).

Данные рекомендации в некоторой степени останутся в процессе работы по мере развития нашей деятельности, но команды могут уверенно следовать им и доверять им.

В случае изменения рекомендаций применяются следующие правила:

  • Существующие API не обязаны быть изменены, но мы рекомендуем это.
  • Клиенты существующих API должны справляться с этими API на основе устаревших правил.
  • Новые API должны соблюдать текущие рекомендации.

Кроме того, следует помнить, что как только API становится публично доступным извне, он должен быть повторно проверен и изменен в соответствии с текущими рекомендациями — ради общей согласованности.

2. Принципы

2.1. Принципы проектирования API

Сравнивая стили интерфейсов веб-сервисов SOA SOAP и REST, первые, как правило, ориентированы на операции, которые обычно специфичны для конкретного случая использования и специализированы. Напротив, REST ориентирован на бизнес-сущности (данные), представленные как ресурсы, которые идентифицируются через URI и могут быть манипулированы с помощью стандартизированных CRUD-подобных методов, использующих различные представления и гипермедиа.

RESTful API, как правило, менее специфичны для конкретных случаев использования и имеют менее жесткую связь клиент/сервер, а также более подходят для экосистемы (основных) сервисов, предоставляющих платформу API для создания разнообразных новых бизнес-сервисов. Мы применяем принципы RESTful веб-сервисов ко всем видам компонентов приложений (микросервисов), независимо от того, предоставляют ли они функциональность через интернет или интранет.

  • Мы предпочитаем API на основе REST с JSON-нагрузкой.
  • Мы предпочитаем, чтобы системы были по-настоящему RESTful [1].

Важным принципом для проектирования и использования API является Закон Постеля, также известный как Принцип Устойчивости (см. также RFC 1122):

Будь либерален в том, что принимаешь; будь консервативен в том, что отправляешь.

Рекомендации к прочтению: Несколько интересных материалов по стилю проектирования RESTful API и архитектуре сервисов:

2.2. API как продукт

Как упоминалось выше, Zalando превращается из интернет-магазина в обширную модную платформу, включающую богатый набор продуктов, следующих модели “Программное обеспечение как платформа” (SaaP) для наших бизнес-партнеров. Как компания, мы хотим предоставлять продукты нашим (внутренним и внешним) клиентам, которые могут использоваться как услуга.

Продукты платформы предоставляют свою функциональность через (публичные) API; следовательно, проектирование наших API должно основываться на принципе “API как продукт”:

  • Относитесь к своему API как к продукту и действуйте как владелец продукта.
  • Поставьте себя на место своих клиентов; будьте защитником их потребностей.
  • Подчеркивайте простоту, понятность и удобство использования API, чтобы сделать их неотразимыми для клиентов-инженеров.
  • Активно улучшайте и поддерживайте согласованность API в долгосрочной перспективе.
  • Используйте отзывы клиентов и обеспечивайте поддержку на уровне обслуживания.

Принятие принципа “API как продукт” способствует созданию экосистемы сервисов, которую легче развивать и использовать для быстрой экспериментирования с новыми бизнес-идеями путем рекомбинации основных возможностей. Это отличает гибкий, инновационный бизнес по предоставлению продуктовых услуг, построенный на платформе API, от обычного бизнеса по интеграции предприятий, где API предоставляются как “приложение” к существующим продуктам для поддержки системной интеграции и оптимизированы для локальной серверной реализации.

Поймите конкретные сценарии использования ваших клиентов и тщательно проверьте компромиссы вариантов вашего API-дизайна с продуктовым мышлением. Избегайте краткосрочных оптимизаций реализации за счет ненужных обязательств со стороны клиента и уделяйте большое внимание качеству API и опыту разработчиков клиентов.

Принцип “API как продукт” тесно связан с нашим принципом “API First” (см. следующую главу), который больше сосредоточен на том, как мы разрабатываем высококачественные API.

2.3. API First

  1. Актуальность принципа “API First”:
    • Принцип “API First” остается краеугольным камнем в Zalando, подчеркивая важность проектирования API до начала кодирования. Это способствует своевременному получению обратной связи, выявлению проблем на ранних этапах и формированию API, не привязанных к конкретным реализациям.
    • Этот подход не противоречит принципам гибкой разработки, а, наоборот, дополняет их, обеспечивая итеративное развитие API с постоянным получением обратной связи.
  1. Детализация “API First”:
    • Определение API вне кода: API должны быть определены с использованием стандартных языков спецификации (например, OpenAPI) до реализации. Это способствует глубокому пониманию предметной области и обобщению бизнес-сущностей.
    • Ранний обзор: Регулярные коллегиальные и клиентские обзоры являются обязательными для обеспечения высокого качества, согласованности дизайна и архитектуры, а также для поддержки независимой разработки клиентских приложений.
    • Единый источник истины: Спецификация API служит единым источником истины, являясь критически важной частью контракта между поставщиком и потребителем сервиса.
    • Инструментальная поддержка: Стандартизированные форматы спецификации облегчают создание инструментов для обнаружения API, пользовательских интерфейсов, документации и автоматических проверок качества.

3. Общие рекомендации

  • Принцип “API First”: `ДОЛЖЕН` следовать принципу “API First”.
  • Спецификация API (OpenAPI): `ДОЛЖЕН` предоставлять спецификацию API с использованием OpenAPI. `СЛЕДУЕТ` использовать OpenAPI 3.0, но поддержка OpenAPI 2.0 (Swagger 2) также допускается. Рекомендуется использовать единый YAML-файл для спецификации.
  • Руководство пользователя API: `СЛЕДУЕТ` предоставлять руководство пользователя API в дополнение к спецификации. Оно должно описывать область применения, примеры использования, обработку ошибок, архитектурный контекст и быть опубликовано в интернете, со ссылкой из спецификации (`#/externalDocs/url`).
  • Язык API: `ДОЛЖЕН` писать API на американском английском (U.S. English).
  • Ссылки: `ДОЛЖЕН` использовать только долговечные и неизменяемые удаленные ссылки, если не указано иное. Для ссылок на фрагменты API, контролируемые Zalando, разрешены специальные URL (`https://infrastructure-api-repository.zalandoapis.com/` и `https://opensource.zalando.com/restful-api-guidelines/{model.yaml}`).

4. Основы REST – Метаинформация

4.1. Должна содержать метаинформацию API

  • `ДОЛЖЕН` содержать следующую метаинформацию OpenAPI:
    • `#/info/title`: уникальное, функционально-описательное имя API.
    • `#/info/version`: для различия версий спецификаций API в соответствии с семантическими правилами.
    • `#/info/description`: подробное описание API.
    • `#/info/contact/{name,url,email}`: сведения об ответственной команде.
  • `ДОЛЖЕН` дополнительно предоставлять следующие свойства расширения OpenAPI:
    • `#/info/x-api-id`: уникальный идентификатор API (см. правило 4.3).
    • `#/info/x-audience`: предполагаемая целевая аудитория API (см. правило 4.4).

4.2. Должна использовать семантическое версионирование

  • `ДОЛЖЕН` соблюдать правила семантического версионирования 2.0 (правила с 1 по 8 и 11) в формате `..`.
    • `MAJOR`: инкрементируется при `несовместимых` изменениях API.
    • `MINOR`: инкрементируется при добавлении `обратно совместимой` функциональности.
    • `PATCH`: опционально инкрементируется при `обратно совместимых` исправлениях ошибок или редакционных изменениях.
  • Предварительные версии (`0.y.z`) могут использоваться для начального проектирования API.

4.3. Должны предоставляться идентификаторы API

  • Каждая спецификация API `ДОЛЖНА` иметь глобально уникальный и неизменяемый идентификатор API в `info`-блоке OpenAPI. Рекомендуется использовать UUID (например, `d0184f38-b98d-11e7-9c56-68f728c1ba70`).
  • Идентификатор `ДОЛЖЕН` соответствовать шаблону `^[a-z0-9][a-z0-9-:.]{6,62}[a-z0-9]$`.

4.4. Должна быть указана аудитория API

  • Каждый API `ДОЛЖЕН` быть классифицирован по предполагаемой целевой аудитории. Допускаются следующие значения (`x-extensible-enum`):
    • `component-internal`
    • `business-unit-internal`
    • `company-internal`
    • `external-partner`
    • `external-public`
  • Это определяется в `#/info/x-audience`. Меньшая группа аудитории не требует дополнительного объявления, если она включена в более широкую.

4.5. Использование функциональной схемы именования

  • В зависимости от аудитории API `ДОЛЖЕН`/`СЛЕДУЕТ`/`МОЖЕТ` следовать функциональной схеме именования:
    • `MUST` (должен): `external-public`, `external-partner`
    • `SHOULD` (следует): `company-internal`, `business-unit-internal`
    • `MAY` (может): `component-internal`
  • Формат: ` ::= -`. Functional Name Registry `ДОЛЖЕН` использоваться для регистрации.

4.6. Должно соблюдаться соглашение об именовании хостов

  • Имена хостов `ДОЛЖНЫ` (или `СЛЕДУЕТ`, в зависимости от аудитории) соответствовать функциональному именованию:
    • `external-public`, `external-partner`: `.zalandoapis.com`
    • Устаревшие конвенции (`..zalan.do`) разрешены только для `component-internal` API.

5. Основы REST – Безопасность

5.1. Все конечные точки должны быть защищены

  • Каждая конечная точка API `ДОЛЖНА` быть защищена аутентификацией и авторизацией.
  • `ДОЛЖЕН` указывать схему безопасности (bearer или oauth2).
  • Для большинства внутренних API рекомендуется использовать `http` тип `Bearer Authentication` (`Authorization: Bearer `).

5.2. Разрешения (скоупы) должны быть определены и назначены

  • Конечные точки `ДОЛЖНЫ` быть оснащены разрешениями, если требуется авторизация клиента (например, для оранжевых/красных данных).
  • Для конечных точек, не требующих специальных разрешений, `ДОЛЖЕН` использовать псевдоразрешение `uid`.

5.3. Должна соблюдаться конвенция именования разрешений (скоупов)

  • Имена разрешений `ДОЛЖНЫ` соответствовать следующему шаблону именования:
    • ``: `.` (например, `order-management.read`)
    • ``: `..` (например, `order-management.sales-order.read`)
    • ``: `uid`
  • `access-mode` включает `read` и `write`.
  • Эта конвенция применяется к скоупам для связи сервис-к-сервису с использованием токенов Platform IAM.

6. Основы REST – Форматы данных

6.1. Должны использоваться стандартные форматы данных

  • `ДОЛЖЕН` всегда использовать стандартные форматы OpenAPI, а также дополнительные форматы Zalando для электронной коммерции (например, для языковых, страновых и валютных кодов).
  • Таблица форматов:
    • `integer`: `int32`, `int64`, `bigint`
    • `number`: `float`, `double`, `decimal`
    • `string`: `byte`, `binary`, `date`, `date-time`, `time`, `duration`, `period`, `password`, `email`, `idn-email`, `hostname`, `idn-hostname`, `ipv4`, `ipv6`, `uri`, `uri-reference`, `uri-template`, `iri`, `iri-reference`, `uuid`, `json-pointer`, `relative-json-pointer`, `regex`, `iso-639-1`, `bcp47`, `iso-3166-alpha-2`, `iso-4217`, `gtin-13`.

6.2. Должен быть определен формат для числовых и целочисленных типов

  • `ДОЛЖЕН` всегда указывать формат (`int32`, `int64`, `bigint` для `integer`; `float`, `double`, `decimal` для `number`).
  • Двоичные данные `ДОЛЖНЫ` быть закодированы в `base64url`.

6.3. Должны использоваться стандартные форматы для свойств даты и времени

  • `ДОЛЖЕН` использовать формат `string` с `date`, `date-time`, `time`, `duration` или `period`.
  • При создании данных `ДОЛЖЕН` использовать заглавную букву `T` как разделитель между датой и временем и заглавную `Z` в конце для обозначения UTC.
  • Пример: `2015-05-28T14:07:17Z`. Избегайте смещений часовых поясов, если это не требуется стандартом (например, HTTP-заголовки).

6.4. Следует выбирать подходящий формат даты или даты-времени

  • `date`: `СЛЕДУЕТ` использовать для свойств, где точное время не требуется (например, даты документов, дни рождения).
  • `date-time`: `СЛЕДУЕТ` использовать для всех остальных случаев, где требуется точная точка во времени.

6.5. Следует использовать стандартные форматы для свойств продолжительности и интервала времени

  • `СЛЕДУЕТ` представлять продолжительность и временные интервалы в виде строк, отформатированных согласно ISO 8601 (RFC 3339).

6.6. Должны использоваться стандартные форматы для свойств страны, языка и валюты

  • `Country codes`: `ISO 3166-1-alpha-2` (например, `DE`).
  • `Language codes`: `ISO 639-1` (например, `en`).
  • `Language variant tags`: `BCP 47` (например, `en-DE`).
  • `Currency codes`: `ISO 4217` (например, `EUR`).

6.7. Следует использовать согласование контента, если клиенты могут выбирать из различных представлений ресурсов

  • Если API поддерживает различные представления ресурсов (JSON, PDF, TEXT, HTML), `СЛЕДУЕТ` использовать согласование контента через заголовки `Accept`, `Accept-Language`, `Accept-Encoding`.
  • `СЛЕДУЕТ` использовать стандартные типы мультимедиа (например, `application/json`, `application/pdf`).

6.8. Следует использовать UUID только при необходимости

  • UUID `СЛЕДУЕТ` использовать только при необходимости крупномасштабной генерации ID (например, в распределенных системах без координации).
  • Избегайте UUID в качестве первичных ключей для справочных данных или данных конфигурации с низким объемом ID.
  • `ДОЛЖЕН` всегда использовать строковый тип для идентификаторов.
  • `СЛЕДУЕТ` рассмотреть использование ULID вместо UUID для пагинации, упорядоченной по времени создания.

7. Основы REST – URL

7.1. Не следует использовать `/api` в качестве базового пути

  • `СЛЕДУЕТ` избегать использования `/api` в качестве базового пути. В большинстве случаев все ресурсы должны быть доступны по корневому пути `/`.
  • Если есть как публичные, так и внутренние API, `СЛЕДУЕТ` поддерживать две различные спецификации API и указывать соответствующую аудиторию.

7.2. Имена ресурсов должны быть во множественном числе

  • `ДОЛЖЕН` использовать имена ресурсов во множественном числе, так как обычно предоставляется коллекция экземпляров ресурсов.
  • Исключение: псевдоидентификатор `self`.

7.3. Должны использоваться URL-дружественные идентификаторы ресурсов

  • Идентификаторы ресурсов `ДОЛЖНЫ` соответствовать регулярному выражению `[a-zA-Z0-9:._\-/]*` (только ASCII-символы: буквы, цифры, подчеркивание, дефис, двоеточие, точка, иногда слеш).
  • Идентификаторы ресурсов `НЕ ДОЛЖНЫ` быть пустыми.

7.4. Сегменты пути должны использовать kebab-case

  • Сегменты пути `ДОЛЖНЫ` быть строками в `kebab-case` (`^[a-z][a-z\-0-9]*$`). Первый символ — строчная буква, последующие — буквы, дефисы или цифры.
  • Пример: `/shipment-orders/{shipment-order-id}`.

7.5. Должны использоваться нормализованные пути без пустых сегментов и конечных слешей

  • `ДОЛЖЕН` не указывать пути с дублирующими или конечными слешами (например, `/customers//addresses` или `/customers/`).
  • Сервисы `СЛЕДУЕТ` реализовать так, чтобы они были устойчивы к ненормализованным путям, нормализуя их перед обработкой.

7.6. URL-адреса должны быть без глаголов

  • `ДОЛЖЕН` использовать только существительные в URL-адресах, так как API описывает ресурсы, а действия выражаются HTTP-методами.
  • Пример: вместо `/cancel-order` используйте `POST /cancellations`.

7.7. Избегайте действий – думайте о ресурсах

  • `ДОЛЖЕН` моделировать API вокруг ресурсных сущностей, используя стандартные HTTP-методы в качестве индикаторов операций (CRUD-подобные операции).
  • Пример: для блокировки статьи используйте `PUT /article-locks/{article-id}` вместо действия `lock`.

7.8. Следует определять полезные ресурсы

  • `СЛЕДУЕТ` определять ресурсы, которые покрывают 90% случаев использования клиентов, содержат необходимую, но минимальную информацию.
  • `СЛЕДУЕТ` поддерживать фильтрацию и встраивание для расширения информации.

7.9. Должны использоваться доменные имена ресурсов

  • `ДОЛЖЕН` использовать доменную номенклатуру для имен ресурсов (например, `sales-order-items` вместо `order-items`). Это улучшает понимание и уменьшает необходимость в дополнительной документации.

7.10. Следует моделировать полные бизнес-процессы

  • API `СЛЕДУЕТ` содержать полные бизнес-процессы со всеми ресурсами, представляющими этот процесс. Это способствует согласованному проектированию и устраняет неявные зависимости между API.

7.11. Ресурсы и подресурсы должны идентифицироваться через сегменты пути

  • `ДОЛЖЕН` идентифицировать ресурсы и подресурсы через сегменты пути: `/resources/{resource-id}/sub-resources/{sub-resource-id}`.
  • Исключение: псевдоидентификатор `self`, если идентификатор ресурса передается через информацию авторизации.

7.12. Могут быть доступны составные ключи в качестве идентификаторов ресурсов

  • Если ресурс лучше всего идентифицируется составным ключом (например, `/shopping-carts/{country}/{session-id}`), `МОЖЕТ` использовать его в URL.
  • Это ограничивает возможность эволюции структуры идентификатора, поэтому API `ДОЛЖЕН` последовательно применять абстракцию составного ключа во всех параметрах запросов и ответов.

7.13. Можно рассмотреть использование (не)вложенных URL

  • `МОЖЕТ` использовать вложенные URL (например, `/shoping-carts/.../cart-items/1`), если подресурс доступен только через родительский ресурс.
  • Если ресурс имеет уникальный глобальный ID, `СЛЕДУЕТ` предоставлять его как ресурс верхнего уровня.

7.14. Следует ограничивать количество типов ресурсов

  • `СЛЕДУЕТ` ограничивать количество типов ресурсов, предоставляемых через один API (рекомендуется 4-8).
  • Ресурсный тип определяется как набор тесно связанных ресурсов (коллекция, ее члены и прямые подресурсы).

7.15. Следует ограничивать количество уровней подресурсов

  • `СЛЕДУЕТ` использовать до 3 уровней подресурсов, чтобы избежать чрезмерной сложности API и длинных URL.

7.16. Параметры запроса должны использовать snake\_case (никогда camelCase)

  • `ДОЛЖЕН` использовать `snake_case` для имен параметров запроса (например, `customer_number`, `sales_order_number`).
  • Имена свойств `ДОЛЖНЫ` соответствовать регулярному выражению `^[a-z_][a-z_0-9]*$`.

7.17. Следует придерживаться общепринятых параметров запроса

  • `ДОЛЖЕН` придерживаться следующих соглашений об именовании для параметров запроса (`REST Design – Pagination`):
    • `q`: общий параметр запроса.
    • `sort`: список полей через запятую, с `+` (по возрастанию) или `-` (по убыванию).
    • `fields`: выражение для получения подмножества полей ресурса.
    • `embed`: выражение для встраивания подсущностей.
    • `offset`: числовое смещение для пагинации.
    • `cursor`: непрозрачный указатель для пагинации.
    • `limit`: предложенный клиентом лимит на количество записей на странице.

8. Основы REST – JSON-нагрузка

8.1. JSON должен использоваться как формат обмена данными нагрузки

  • `ДОЛЖЕН` использовать JSON (RFC 7159) для представления структурированных данных.
  • JSON-нагрузка `ДОЛЖНА` использовать JSON-объект в качестве структуры данных верхнего уровня.
  • `ДОЛЖЕН` также соответствовать RFC 7493 (UTF-8, действительные Unicode-строки, уникальные имена членов).

8.2. Следует проектировать единую схему ресурсов для чтения и записи

  • `СЛЕДУЕТ` использовать общую модель для чтения и записи одного и того же типа ресурса.
  • Различия `СЛЕДУЕТ` учитывать с помощью `writeOnly` (только для запроса) и `readOnly` (только для ответа).

8.3. Следует учитывать сервисы, не полностью поддерживающие JSON/Unicode

  • Серверы и клиенты, перенаправляющие JSON-содержимое на другие инструменты, `СЛЕДУЕТ` проверять полную поддержку JSON или Unicode. Если нет, `СЛЕДУЕТ` отклонять или санировать неподдерживаемые символы.

8.4. Может передавать не-JSON типы носителей с использованием специфичных для данных стандартных форматов

  • `МОЖЕТ` поддерживать не-JSON типы носителей, если используются специфичные для бизнес-объектов стандартные форматы (например, изображения, документы, архивы).
  • Общие форматы, кроме JSON (XML, CSV), `МОГУТ` предоставляться только дополнительно к JSON в качестве формата по умолчанию с использованием согласования контента.

8.5. Следует использовать стандартные типы носителей

  • `СЛЕДУЕТ` использовать стандартные типы носителей IANA (например, `application/json`, `application/problem+json`).
  • Избегайте пользовательских типов (`application/x.zalando.article+json`). Исключение: версионирование конечных точек API.

8.6. Имена массивов должны быть во множественном числе

  • `СЛЕДУЕТ` использовать имена массивов во множественном числе (например, `items`), а имена объектов — в единственном (например, `item`).

8.7. Имена свойств должны быть camelCase (не snake_case)

  • ИЗМЕНЕНИЕ ОТ ОРИГИНАЛЬНОГО РУКОВОДСТВА: В отличие от исходного руководства, которое предписывало `snake_case`, данное обновленное руководство `РЕКОМЕНДУЕТ` использовать **camelCase для имен свойств в JSON-нагрузке. Это соответствует большинству современных гайдлайнов по проектированию API (например, Google, Microsoft, AWS) и более гармонично сочетается с распространенными языками программирования (Java, JavaScript, C#), где `camelCase` является стандартной конвенцией для имен переменных и полей.
    • Пример: `customerNumber`, `salesOrderNumber`, `billingAddress`.
  • Обоснование изменения:** Хотя `snake_case` был принят некоторыми крупными компаниями, `camelCase` имеет более широкое распространение и лучшую интеграцию с современными инструментами и библиотеками, что упрощает разработку и поддержку клиентских приложений. `camelCase` также способствует более естественному чтению JSON-нагрузки в коде, написанном на большинстве объектно-ориентированных и скриптовых языков.
  • Исключение:** Для параметров запроса и заголовков HTTP `ДОЛЖЕН` по-прежнему использовать `snake_case` и `kebab-case` соответственно, как указано в соответствующих разделах.

8.8. Следует объявлять значения перечислений с использованием формата строки UPPER_SNAKE_CASE

  • `СЛЕДУЕТ` использовать `UPPER_SNAKE_CASE` для значений перечислений (например, `VALUE`, `YET_ANOTHER_VALUE`).
  • Исключение: чувствительные к регистру значения из внешних источников (например, коды языков ISO 639-1) или значения `sort`
    параметра.

8.9. Следует использовать соглашение об именовании для свойств даты/времени

  • Имена свойств даты и времени `СЛЕДУЕТ` заканчивать на `_at` или содержать `date`, `time`, `timestamp`.
  • Примеры: `created_at`, `modified_at`, `arrival_date`, `campaign_start_time`.

8.10. Следует определять карты с использованием additionalProperties

  • `СЛЕДУЕТ` представлять карты (отображения строковых ключей на другие типы) с использованием `additionalProperties` в схеме OpenAPI.
  • Пример: `translations` в объекте сообщения.

8.11. Должна использоваться одинаковая семантика для ‘null’ и отсутствующих свойств

  • Если свойство может быть `null` или отсутствовать, `ДОЛЖЕН` обрабатывать обе ситуации одинаково.
  • Это позволяет избежать ошибок из-за тонких различий в семантике, которые могут быть неправильно интерпретированы клиентами.
  • Исключение: `JSON Merge Patch` (RFC 7396) использует `null` для удаления свойства.

8.12. Не рекомендуется использовать ‘null’ для булевых свойств

  • `НЕ РЕКОМЕНДУЕТСЯ` использовать `null` для булевых свойств. Если `null` имеет смысл, `СЛЕДУЕТ` использовать перечисление (например, `YES`, `NO`, `UNDEFINED`).

8.13. Не рекомендуется использовать ‘null’ для пустых массивов

  • `НЕ РЕКОМЕНДУЕТСЯ` использовать `null` для пустых массивов. Используйте пустой список `[]`.

8.14. Должны использоваться общие имена полей и семантика

  • `ДОЛЖЕН` использовать общие имена полей и семантику:
    • `id`: идентификатор объекта (строка, непрозрачная, уникальна в контексте).
    • `xyz_id`: идентификатор другого объекта (например, `partner_id`).
    • `etag`: ETag для оптимистической блокировки.
  • Примеры: `created_at`, `modified_at`, `parent_node_id`.

8.15. Должны использоваться общие поля адреса

  • `ДОЛЖЕН` использовать стандартные имена и семантику для всех атрибутов, относящихся к информации об адресе (например, `addressee`, `address` с полями `street`, `city`, `zip`, `country_code`).

8.16. Должен использоваться общий денежный объект

  • `ДОЛЖЕН` использовать следующую общую структуру для денежных объектов:
    ```yaml
    Money:
    type: object
    properties:
    amount:
    type: number
    format: decimal
    currency:
    type: string
    format: iso-4217
    required:
    • amount
    • currency
      ```
  • `ДОЛЖЕН` рассматривать `Money` как закрытый тип данных, предпочитая композицию наследованию.
  • `ДОЛЖЕН` обеспечивать высокую точность (`decimal`) и не использовать `float` или `double` для расчетов.

9. Основы REST – HTTP-запросы

9.1. HTTP-методы должны использоваться правильно

  • `ДОЛЖЕН` соблюдать стандартизированную HTTP-семантику (RFC-9110 “HTTP Semantics”).
  • GET:
    • `ДОЛЖЕН` использоваться для чтения одиночного или коллективного ресурса.
    • Возвращает 404, если ресурс не существует (для одиночных).
    • Возвращает 200 (коллекция пуста) или 404 (коллекция отсутствует) для коллекций.
    • `НЕ ДОЛЖЕН` иметь тела запроса.
    • GET с телом запроса**: Если требуется передача сложной структурированной информации с `GET`, а использование query-параметров невозможно (из-за ограничений на размер), `СЛЕДУЕТ` использовать `POST` с телом запроса и явно документировать это.
      • `НЕ ДОЛЖЕН` использовать заголовки для передачи сложной структурированной информации из-за ненадежных ограничений на размер.
  • PUT:
    • `ДОЛЖЕН` использоваться для полного обновления (и иногда создания) ресурсов (одиночных или коллективных).
    • Семантика: “пожалуйста, поместите приложенное представление по URI, заменяя любой существующий ресурс”.
    • Обычно применяется к одиночным ресурсам.
    • Как правило, `УСТОЙЧИВ` к отсутствию ресурсов, неявно создавая их перед обновлением.
    • При успешном выполнении сервер `ЗАМЕНЯЕТ` весь ресурс.
    • Успешные `PUT` запросы возвращают 200 или 204 (без возврата ресурса), 201 (если ресурс был создан), 202 (если запрос принят для асинхронной обработки).
    • `СЛЕДУЕТ` отдавать предпочтение `POST` для создания (как минимум ресурсов верхнего уровня), а `PUT` — для обновлений. Если идентификатор ресурса полностью контролируется клиентом, `МОЖЕТ` использовать `PUT` для создания, передавая идентификатор в качестве параметра пути.
    • Повторное выполнение `PUT` *должно быть* идемпотентно.
  • POST:
    • Обычно**: `ДОЛЖЕН` использоваться для создания одиночных ресурсов на конечной точке коллекции (`add the enclosed representation to the collection resource`).
    • Редко**: `МОЖЕТ` использоваться для выполнения хорошо специфицированного запроса на одиночном ресурсе (`execute the given well specified request on the resource identified by the URL`).
    • successful `POST` на коллекции создает один или несколько экземпляров ресурсов.
    • Для одиночных ресурсов `СЛЕДУЕТ` возвращать 201 и новый объект ресурса (включая идентификатор).
    • URL для получения ресурса `СЛЕДУЕТ` предоставлять в заголовке `Location`.
    • Идентификатор ресурса `НЕ ДОЛЖЕН` передаваться клиентом в теле запроса, а `ДОЛЖЕН` создаваться и поддерживаться сервисом.
    • Для нескольких ресурсов `СЛЕДУЕТ` возвращать 201, если они создаются атомарно.
    • `ДОЛЖЕН` использовать код 207 для пакетных или массовых запросов, если запрос может частично завершиться неудачей.
    • Повторное выполнение `POST` *не обязательно* является идемпотентным, но `СЛЕДУЕТ` `РАССМОТРЕТЬ` проектирование его как идемпотентного.
    • `МОЖЕТ` поддерживать асинхронную обработку запросов (статус 202).
  • PATCH:
    • `ДОЛЖЕН` использоваться для частичного обновления объектов ресурсов.
    • Тело запроса `ДОЛЖНО` содержать документ исправления (patch document) с определенным типом носителя.
    • Успешные `PATCH` возвращают 200, 204 или 202.
    • `СЛЕДУЕТ` выбирать один из следующих шаблонов (порядок предпочтения):
      1. `PUT` с полными объектами.
      2. `PATCH` с `JSON Merge Patch` (`application/merge-patch+json`).
      3. `PATCH` с `JSON Patch` (`application/json-patch+json`).
      4. `POST` (с явным описанием).
    • Повторное выполнение `PATCH` *не обязательно* является идемпотентным, но `СЛЕДУЕТ` `РАССМОТРЕТЬ` проектирование его как идемпотентного.
  • DELETE:
    • `ДОЛЖЕН` использоваться для удаления ресурсов.
    • Обычно применяется к одиночным ресурсам.
    • `МОЖЕТ` применяться к нескольким ресурсам одновременно с использованием параметров запроса.
    • Успешные `DELETE` возвращают 200, 204 или 202.
    • Неуспешные `DELETE` возвращают 404 (не найден) или 410 (уже удален).
    • После `DELETE` `GET` на ресурс `ДОЛЖЕН` возвращать 404 или 410.
    • DELETE с query-параметрами**: `МОЖЕТ` иметь query-параметры в качестве фильтров.
    • DELETE с телом запроса**: Если требуется дополнительная информация, не являющаяся фильтрами, `РЕКОМЕНДУЕТСЯ` использовать `POST`, так как семантика `DELETE` с телом не определена RFC-9110.
  • HEAD:
    • `ДОЛЖЕН` использоваться для получения информации заголовка для одиночных ресурсов и коллекций.
    • Имеет ту же семантику, что и `GET`, но возвращает только заголовки, без тела.
  • OPTIONS:
    • `МОЖЕТ` использоваться для проверки доступных операций (HTTP-методов) для данной конечной точки.
    • Ответы `OPTIONS` обычно возвращают список методов в заголовке `Allow`.

9.2. Должны выполняться общие свойства методов

  • Реализации методов `ДОЛЖНЫ` соответствовать следующим свойствам согласно RFC 9110 Section 9.2:
    • `Safe`: `GET`, `HEAD`, `OPTIONS`, `TRACE`
    • `Idempotent`: `GET`, `HEAD`, `PUT`, `DELETE`, `OPTIONS`, `TRACE` (для `POST`/`PATCH` `СЛЕДУЕТ` рассмотреть идемпотентность)
    • `Cacheable`: `GET`, `HEAD` (для `POST` `МОЖЕТ` быть, но редко поддерживается)

9.3. Следует рассмотреть проектирование POST и PATCH как идемпотентных операций

  • `СЛЕДУЕТ` рассмотреть проектирование `POST` и `PATCH` как идемпотентных, чтобы предотвратить дублирование ресурсов или потерю обновлений.
  • Применяемые шаблоны:
    • Conditional key (условный ключ):** через заголовок `If-Match` (например, для `ETag`). Предотвращает дублирование и потерю обновлений.
    • Secondary key (вторичный ключ):** через свойство ресурса в теле запроса. Хранится постоянно в ресурсе, обеспечивает идемпотентность при создании (избегает дубликатов).
    • Idempotency-Key header:** клиентский ключ через заголовок `Idempotency-Key` (временный). Обеспечивает точно такой же ответ для повторных запросов.
  • Пример использования вторичного ключа: `shopping cart ID` в ресурсе заказа.

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

  • `СЛЕДУЕТ` использовать вторичный ключ, предоставляемый в теле запроса, для идемпотентного создания `POST`, чтобы избежать дублирования ресурсов.
  • Вторичный ключ хранится постоянно в ресурсе как альтернативный или комбинированный ключ, защищенный ограничением уникальности.

9.5. Может поддерживать асинхронную обработку запросов

  • `МОЖЕТ` использовать асинхронную обработку для долгих запросов.
  • `РЕКОМЕНДУЕТСЯ` представлять асинхронную обработку через ресурс “job” со статусом (например, `POST /report-jobs` возвращает 201 с `job-id` и URL в `Location`).
  • Если нет отдельного ресурса “job”, `МОЖЕТ` использовать 202 с заголовком `Location`, указывающим на отчет.
  • `НЕ ДОЛЖЕН` использовать 204 или 404 вместо 202.

9.6. Формат коллекции заголовков и параметров запроса должен быть определен

  • `ДОЛЖЕН` явно определить формат коллекции (либо список через запятую, либо повторение параметра) в спецификации OpenAPI, так как OpenAPI не поддерживает оба варианта одновременно.
  • Для заголовков: `style: simple, explode: false` (не разрешено).
  • Для запросов: `style: form, explode: false` или `style: form, explode: true`.

9.7. Следует проектировать простые языки запросов с использованием параметров запроса

  • `СЛЕДУЕТ` использовать параметры запроса для простых языков запросов, так как это нативно для HTTP, легко расширяемо и хорошо поддерживается.
  • Для API, используемых внешними командами, `СЛЕДУЕТ` использовать простые языки запросов. Для внутренних или специфичных случаев `МОЖЕТ` использовать более сложные.
  • Примеры: `name=Zalando`, `color=red,green`, `limit=5`, `created_after=2019-07-17`.

9.8. Следует проектировать сложные языки запросов с использованием JSON

  • Для сложных языков запросов (например, для поиска, каталогов продуктов) с большим количеством фильтров, динамическими фильтрами или сложными операторами (`and`, `or`, `not`), `СЛЕДУЕТ` использовать вложенные JSON-структуры.
  • Пример: JSON-документ для сложного запроса с `and` и `or`.
  • Подразумевает использование шаблона `GET` с телом запроса.

9.9. Должна быть документирована неявная фильтрация ответов

  • Если в коллекции ресурсов или запросах применяется неявная фильтрация (например, по авторизации пользователя), `ДОЛЖЕН` документировать это в описании конечной точки в спецификации API.
  • Пример: `GET /business-partners` возвращает только партнеров, доступных текущему пользователю.

10. Основы REST – Коды состояния HTTP

10.1. Должны использоваться официальные коды состояния HTTP

  • `ДОЛЖЕН` использовать только официальные коды состояния HTTP (из RFC и IANA Status Code Registry) и в соответствии с их семантикой.
  • Избегайте неофициальных кодов (например, от Nginx).

10.2. Должны быть указаны успешные и ошибочные ответы

  • `ДОЛЖЕН` определить все успешные и специфичные для сервиса ошибочные ответы в спецификации API.
  • Описания ошибок `ДОЛЖНЫ` предоставлять информацию об условиях, приведших к ошибке.
  • Исключение: стандартные клиентские и серверные ошибки (401, 403, 404, 500, 503) не требуют индивидуального определения, если их семантика очевидна.

10.3. Следует использовать только наиболее распространенные коды состояния HTTP

  • `СЛЕДУЕТ` использовать только наиболее распространенные и понятные коды (200, 201, 202, 204, 207, 304, 400, 401, 404, 405, 406, 409, 412, 415, 428, 429, 500, 501, 502, 503, 504).
  • Избегайте менее распространенных кодов (`205`, `206`, `301`, `302`, `303`, `307`, `308`, `408`, `410`, `411`, `417`, `418`, `422`, `423`, `424`, `431`, `505`, `507`, `511`), если они не имеют четкой, специфичной для API семантики.
  • Коды, которые не возвращаются API, `НЕ ДОЛЖНЫ` документироваться.

10.4. Должны использоваться наиболее специфичные коды состояния HTTP

  • `ДОЛЖЕН` использовать наиболее специфичные коды состояния HTTP при возврате информации о статусе обработки запроса или ошибках.

10.5. Должен использоваться код 207 для пакетных или массовых запросов

  • `ДОЛЖЕН` использовать 207 (Multi-Status) для пакетных или массовых запросов, если могут быть частичные неудачи или требуется информация о статусе для каждого элемента.
  • Ответ с 207 `ДОЛЖЕН` содержать `multi-status response` в теле, включающий `id`, `status` и `description` для каждого элемента.

10.6. Должен использоваться код 429 с заголовками для ограничения скорости

  • `ДОЛЖЕН` использовать 429 (Too Many Requests) при превышении лимита запросов.
  • `ДОЛЖЕН` включать заголовки `Retry-After` (с задержкой в секундах) или `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` (время в секундах до сброса).

10.7. Должна поддерживаться проблема JSON

  • `ДОЛЖЕН` поддерживать `Problem JSON` (RFC 9457, `application/problem+json`) для предоставления расширяемой информации об ошибках.
  • Каждая конечная точка `ДОЛЖНА` быть способна возвращать `Problem JSON` для ошибок 4xx и 5xx.
  • Клиенты `ДОЛЖНЫ` быть устойчивы и не зависеть от возврата `Problem JSON`.
  • `МОЖЕТ` определять пользовательские типы проблем для более подробной информации об ошибках.
  • Идентификаторы типов и экземпляров проблем `НЕ ПРЕДНАЗНАЧЕНЫ` для разрешения (`URI references`), но `МОГУТ` указывать на документацию.

10.8. Не должны раскрываться трассировки стека

  • `НЕ ДОЛЖЕН` раскрывать трассировки стека в ответах, так как они содержат детали реализации, утечки конфиденциальной информации и информацию об уязвимостях.

10.9. Не следует использовать коды перенаправления

  • `НЕ СЛЕДУЕТ` использовать коды перенаправления (кроме 304). Перенаправление трафика `ЛУЧШЕ` выполнять на уровне API (например, через обратный прокси) без участия клиента.
  • Для идемпотентных `POST` запросов, где ресурс уже существует, `СЛЕДУЕТ` возвращать 200 с заголовком `Location`.
  • Для неидемпотентных `POST` запросов, где ресурс не может быть создан повторно, `СЛЕДУЕТ` возвращать 409.

11. Основы REST – HTTP-заголовки

11.1. Использование стандартных определений заголовков

  • `СЛЕДУЕТ` использовать стандартные определения HTTP-заголовков из руководства для упрощения API.
  • Пример: `parameters` или `headers` в `components`.

11.2. Могут использоваться стандартные заголовки

  • API `МОГУТ` использовать HTTP-заголовки, определенные не устаревшими RFC. Поддерживаемые заголовки `ДОЛЖНЫ` быть явно упомянуты в спецификации API.

11.3. Следует использовать kebab-case с прописными буквами для HTTP заголовков

  • `СЛЕДУЕТ` использовать `kebab-case` с прописными буквами для первых букв каждого слова (например, `If-Modified-Since`, `Accept-Encoding`, `Content-ID`).
  • Исключение: общепринятые сокращения, такие как `ID`.
  • Заголовки HTTP нечувствительны к регистру, но это для согласованности.

11.4. Заголовки `Content-*` должны использоваться правильно

  • `ДОЛЖЕН` правильно использовать заголовки `Content-*`, которые описывают содержимое тела сообщения и могут использоваться как в запросах, так и в ответах.
  • Примеры: `Content-Disposition`, `Content-Encoding`, `Content-Length`, `Content-Language`, `Content-Location`, `Content-Range`, `Content-Type`.

11.5. Следует использовать заголовок `Location` вместо `Content-Location`

  • `СЛЕДУЕТ` использовать заголовок `Location` вместо `Content-Location` для указания местоположения ресурса в ответах на создание или перенаправление.
  • Это позволяет избежать сложностей и двусмысленности, связанных с `Content-Location`.

11.6. Может использоваться заголовок `Content-Location`

  • `МОЖЕТ` использовать `Content-Location` в успешных операциях записи (`PUT`, `POST`, `PATCH`) и чтения (`GET`, `HEAD`) для управления кэшированием или указания фактического местоположения переданного ресурса.
  • При использовании `Content-Location` `Content-Type` `ДОЛЖЕН` быть также установлен.

11.7. Может рассматриваться поддержка заголовка Prefer для управления предпочтениями обработки

  • `МОЖЕТ` рассматривать поддержку заголовка `Prefer` (RFC 7240), который позволяет клиентам запрашивать желаемое поведение обработки с сервера (например, `respond-async`, `return=minimal`, `return=total-count`, `wait`, `handling=strict`).
  • Поддержка `Prefer` является опциональной и `РЕКОМЕНДУЕТСЯ` по сравнению с проприетарными заголовками `X-`.

11.8. Может рассматриваться поддержка ETag вместе с заголовками If-Match / If-None-Match

  • `МОЖЕТ` рассматривать поддержку заголовка `ETag` вместе с `If-Match` и `If-None-Match` для обнаружения конфликтов при конкурентных операциях обновления (`PUT`, `POST`, `PATCH`).
  • `If-Match`: для проверки, соответствует ли версия обновляемой сущности запрошенному `ETag`. Если нет, возвращает 412.
  • `If-None-Match`: для обнаружения конфликтов при создании ресурсов. Если найдена соответствующая сущность, возвращает 412.

11.9. Может рассматриваться поддержка заголовка Idempotency-Key

  • `МОЖЕТ` рассматривать поддержку заголовка `Idempotency-Key` для обеспечения строгой идемпотентности (одинаковые ответы для повторных запросов) при создании или обновлении ресурсов.
  • Клиент предоставляет уникальный ключ запроса, который временно хранится на сервере вместе с ответом.
  • Ключи `ДОЛЖНЫ` иметь достаточную энтропию (например, UUID v4). Истекают через 24 часа.

11.10. Следует использовать только указанные проприетарные Zalando заголовки

  • `СЛЕДУЕТ` избегать проприетарных HTTP-заголовков, но `МОЖЕТ` использовать разрешенные Zalando проприетарные заголовки для передачи общей, неспецифичной для бизнеса контекстной информации (например, `X-Flow-ID`, `X-Tenant-ID`, `X-Sales-Channel`, `X-Frontend-Type`, `X-Device-Type`, `X-Device-OS`, `X-Mobile-Advertising-ID`).
  • Имена `ДОЛЖНЫ` начинаться с `X-` и использовать `dash-case`.
  • Исключение: `X-RateLimit-*` заголовки.

11.11. Проприетарные заголовки должны распространяться

  • Все проприетарные заголовки Zalando, определенные в 11.10, `ДОЛЖНЫ` распространяться по всей цепочке вызовов (end-to-end) без изменений.
  • Значения `МОГУТ` влиять на результаты запросов.

11.12. Должен поддерживаться X-Flow-ID

  • `ДОЛЖЕН` поддерживать `X-Flow-ID` в запросах RESTful API и поле `flow_id` в событиях.
  • `X-Flow-ID` — это общий параметр для отслеживания потоков вызовов через систему и корреляции действий сервисов.
  • Допустимые форматы: UUID (RFC-4122), base64 (RFC-4648), base64url (RFC-4648 Section 5), случайная строка (ASCII, макс. 128 символов).
  • Сервисы `ДОЛЖНЫ` создавать новый `Flow-ID`, если он не предоставлен.
  • Сервисы `ДОЛЖНЫ` распространять `Flow-ID` (передавать во все дальнейшие вызовы API и события, записывать в логи).

12. Проектирование REST – Гипермедиа

12.1. Должен использоваться уровень зрелости REST 2

  • `ДОЛЖЕН` стремиться к хорошей реализации уровня зрелости REST 2 (использование HTTP-глаголов и кодов состояния).
  • Это означает: `ДОЛЖЕН` избегать действий в URL, `ДОЛЖЕН` оставлять URL без глаголов, `ДОЛЖЕН` правильно использовать HTTP-методы, `СЛЕДУЕТ` использовать только наиболее распространенные коды состояния HTTP.

12.2. Может использоваться уровень зрелости REST 3 – HATEOAS

  • `НЕ РЕКОМЕНДУЕТСЯ` реализовывать уровень зрелости REST 3 (HATEOAS) в общем случае, так как он добавляет сложность без явной ценности в контексте SOA Zalando.
  • Основные опасения: HATEOAS не добавляет ценности для самоописания API в условиях API First; общие клиенты HATEOAS не работают на практике в SOA; спецификации OpenAPI не поддерживают HATEOAS в достаточной мере.
  • `МОЖЕТ` использовать HATEOAS только в случае, если выявлена явная ценность, оправдывающая дополнительную сложность.

12.3. Должны использоваться общие элементы управления гипертекстом

  • При встраивании ссылок на другие ресурсы в представления `ДОЛЖЕН` использовать общий объект `hypertext control`.
  • Объект `ДОЛЖЕН` содержать как минимум атрибут `href`: URI ресурса, на который указывает ссылка (только HTTP(s)).
  • Имя атрибута, содержащего `HttpLink` объект, `ДОЛЖНО` указывать на связь между ссылающимся и ссылаемым ресурсами.
  • `СЛЕДУЕТ` использовать имена из IANA Link Relation Registry. Дефисы в IANA-именах `ДОЛЖНЫ` быть заменены на подчеркивания (`snake_case`).

12.4. Следует использовать простые элементы управления гипертекстом для пагинации и само-ссылок

  • Для пагинации и само-ссылок `СЛЕДУЕТ` использовать упрощенную форму расширяемых общих гипертекстовых элементов управления, состоящую из простого URI-значения в сочетании с соответствующими отношениями ссылок (например, `next`, `prev`, `first`, `last`, `self`).

12.5. Для идентификации ресурсов должен использоваться полный, абсолютный URI

  • Ссылки на другие ресурсы `ДОЛЖНЫ` всегда использовать полный, абсолютный URI.
  • Это снижает сложность на стороне клиента и избегает двусмысленности с базовыми URI.

12.6. Не следует использовать заголовки ссылок с JSON-сущностями

  • `НЕ ДОЛЖЕН` использовать заголовки `Link` (RFC 8288) в сочетании с JSON-носителями. Ссылки `ПРЕДПОЧТИТЕЛЬНЕЕ` встраивать непосредственно в JSON-нагрузку.

13. Проектирование REST – Производительность

13.1. Следует снижать потребность в пропускной способности и улучшать отзывчивость

  • API `СЛЕДУЕТ` поддерживать методы для снижения пропускной способности и ускорения ответа, особенно для мобильных клиентов.
  • Общие методы: gzip-сжатие, фильтрация полей, ETag (для условных запросов), заголовок `Prefer`, пагинация, кэширование мастер-данных.

13.2. Следует использовать gzip-сжатие

  • Серверы и клиенты `СЛЕДУЕТ` поддерживать кодирование содержимого `gzip` для уменьшения передаваемых данных и ускорения ответа.
  • Серверы `ДОЛЖНЫ` также поддерживать несжатый контент для тестирования.
  • Спецификация API `СЛЕДУЕТ` определять `Accept-Encoding` и `Content-Encoding`.

13.3. Следует поддерживать частичные ответы через фильтрацию

  • `СЛЕДУЕТ` поддерживать фильтрацию возвращаемых полей сущностей с помощью параметра запроса `fields`.
  • Пример: `GET http://api.example.org/users/123?fields=(name,friends(name))`
  • OpenAPI не поддерживает формальное описание различных схем, поэтому `СЛЕДУЕТ` документировать это в описании конечной точки.

13.4. Следует разрешать опциональное встраивание подресурсов

  • `СЛЕДУЕТ` разрешать встраивание связанных ресурсов (расширение ресурсов) для уменьшения количества запросов, используя параметр запроса `embed`.
  • Пример: `GET /order/123?embed=(items)`.

13.5. Должны быть документированы кэшируемые конечные точки GET, HEAD и POST

  • Кэширование на стороне клиента и прозрачное веб-кэширование `СЛЕДУЕТ` избегать, если только сервис не требует его для самозащиты.
  • По умолчанию, серверы и клиенты `ДОЛЖНЫ` устанавливать `Cache-Control: no-store`.
  • Если кэширование требуется, `ДОЛЖЕН` документировать все кэшируемые конечные точки `GET`, `HEAD` и `POST` путем объявления поддержки заголовков `Cache-Control`, `Vary` и `ETag` в ответе.
  • `Default Cache-Control`: `private`, `must-revalidate`, `max-age`.
  • `Default Vary`: `accept`, `accept-encoding`.
  • `РЕКОМЕНДУЕТСЯ` прикреплять кэш непосредственно к сервисной (или шлюзовой) слою приложения.

14. Проектирование REST – Пагинация

14.1. Должна поддерживаться пагинация

  • `ДОЛЖНА` поддерживаться пагинация для доступа к спискам данных, чтобы защитить сервис от перегрузки и поддержать итерацию на стороне клиента.
  • `ДОЛЖЕН` придерживаться общих имен параметров запроса (`offset`, `cursor`, `limit`).

14.2. Следует предпочитать пагинацию на основе курсоров, избегать пагинации на основе смещения

  • `СЛЕДУЕТ` предпочитать пагинацию на основе курсоров из-за ее эффективности, особенно для больших объемов данных и NoSQL-баз данных.
  • Курсор `ДОЛЖЕН` быть непрозрачной строкой для клиентов, шифрующей (или кодирующей) позицию страницы, направление и примененные фильтры запроса.

14.3. Следует использовать объект страницы ответа пагинации

  • `СЛЕДУЕТ` использовать объект страницы ответа с полями `self`, `first`, `prev`, `next`, `last` (`uri|cursor`).
  • `ДОЛЖЕН` включать поле `items` для содержимого страницы.
  • `МОЖЕТ` включать поле `query` для примененных фильтров запроса, если используется `GET` с телом.

14.4. Следует использовать ссылки пагинации

  • `СЛЕДУЕТ` поддерживать упрощенные гипертекстовые элементы управления в качестве стандартных ссылок пагинации (полные URL с параметрами).
  • Пример: `{“self”: “https://my-service.zalandoapis.com/resources?cursor=”, ...}`.

14.5. Следует избегать общего количества результатов

  • `СЛЕДУЕТ` избегать предоставления общего количества результатов в ответах пагинации, так как его расчет является дорогостоящей операцией.
  • Если клиент действительно требует общее количество результатов, `МОЖЕТ` поддерживать это требование через заголовок `Prefer` с директивой `return=total-count`.

15. Проектирование REST – Совместимость

15.1. Обратная совместимость не должна нарушаться

  • `ДОЛЖЕН` изменять API, сохраняя при этом работоспособность всех потребителей. API — это контракты, которые не могут быть нарушены в одностороннем порядке.
  • Используйте совместимые расширения или новые версии API с поддержкой старых.

15.2. Следует предпочитать совместимые расширения

  • `СЛЕДУЕТ` применять следующие правила:
    • Добавлять только опциональные, никогда не обязательные поля.
    • Никогда не изменять семантику полей.
    • Никогда не делать логику валидации более строгой.
    • Диапазоны перечислений (`enum`) могут быть уменьшены для входных параметров, если сервер поддерживает старые значения. Для выходных параметров `НЕ МОГУТ` быть расширены.
    • `СЛЕДУЕТ` использовать `x-extensible-enum` для перечислений, которые будут расширяться.

15.3. Следует проектировать API консервативно

  • Разработчики `СЛЕДУЕТ` быть консервативными и точными в том, что они принимают от клиентов:
    • Неизвестные входные поля `НЕ СЛЕДУЕТ` игнорировать; серверы `СЛЕДУЕТ` возвращать 400.
    • `ДОЛЖЕН` быть точными в определении ограничений входных данных и возвращать специальные ошибки при их нарушении.
    • `ПРЕДПОЧТИТЕЛЬНО` быть более специфичным и ограничительным.

15.4. Клиенты должны быть готовы принимать совместимые расширения API

  • Клиенты сервисов `ДОЛЖНЫ` применять принцип устойчивости:
    • Быть консервативными с запросами API и входными данными.
    • Быть толерантными при обработке и чтении данных ответов API: игнорировать новые поля в полезной нагрузке, не удалять их для последующих `PUT` запросов.
    • Быть готовыми к тому, что `x-extensible-enum` параметры могут возвращать новые значения; быть агностиками или предоставлять поведение по умолчанию для неизвестных значений.
    • Быть готовыми обрабатывать коды состояния HTTP, явно не указанные в определениях конечных точек.
    • Следовать перенаправлениям (301).

15.5. Спецификация OpenAPI должна по умолчанию рассматриваться как открытая для расширения

  • `ДОЛЖЕН` рассматривать определения объектов OpenAPI как открытые для расширения по умолчанию (как в JSON-Schema 5.18 `additionalProperties`).
  • Клиенты `НЕ ДОЛЖНЫ` предполагать, что объекты закрыты для расширения без `additionalProperties` и `ДОЛЖНЫ` игнорировать неизвестные поля.
  • Серверы, получающие неожиданные данные, `СЛЕДУЕТ` отклонять запросы с неопределенными полями, чтобы сигнализировать клиентам, что эти поля не будут сохранены.
  • Форматы API `НЕ ДОЛЖНЫ` объявлять `additionalProperties` как `false`.

15.6. Следует избегать версионирования

  • `СЛЕДУЕТ` избегать создания дополнительных версий API.
  • Если изменение API несовместимо, `СЛЕДУЕТ` создать новый ресурс (вариант), новый сервис или новую версию API.
  • Настоятельно `РЕКОМЕНДУЕТСЯ` использовать первые два подхода.

15.7. Должно использоваться версионирование типа носителя

  • Если версионирование API неизбежно, `ДОЛЖЕН` использовать версионирование типа носителя (`media type versioning`, например, `application/x.zalando.cart+json;version=2`).
  • Это менее тесно связано и поддерживает согласование контента.
  • Версионирование применяется только к схеме полезной нагрузки, а не к URI или семантике метода.
  • `СЛЕДУЕТ` включать `Content-Type` в заголовок `Vary`.

15.8. Не следует использовать версионирование URL

  • `НЕ ДОЛЖЕН` использовать версионирование URL (например, `/v1/customers`). Это приводит к более тесной связи и усложняет управление релизами. Вместо этого `ДОЛЖЕН` использовать версионирование типа носителя.

15.9. Всегда должны возвращаться JSON-объекты как структуры данных верхнего уровня

  • `ДОЛЖЕН` всегда возвращать JSON-объект (а не массив) в качестве структуры данных верхнего уровня в теле ответа, чтобы обеспечить будущую расширяемость.
  • Карты (`additionalProperties`), хотя и являются технически объектами, также `ЗАПРЕЩЕНЫ` как структуры верхнего уровня, поскольку они не поддерживают совместимые будущие расширения.

15.10. Следует использовать открытый список значений (x-extensible-enum) для типов перечислений

  • `СЛЕДУЕТ` использовать `x-extensible-enum` для перечислений, которые, вероятно, будут расширяться.
  • Это позволяет избежать проблем совместимости, присущих `enum` JSON-схемы (которые по определению являются закрытым набором значений).
  • Клиенты `ДОЛЖНЫ` быть готовы к расширениям перечислений и реализовать запасное/поведение по умолчанию для неизвестных значений.

16. Проектирование REST – Устаревшая функциональность

16.1. Устаревшая функциональность должна быть отражена в спецификациях API

  • `ДОЛЖЕН` установить `deprecated: true` для устаревших элементов (конечная точка, параметр, объект схемы, свойство схемы).
  • Если планируется отключение, `ДОЛЖЕН` указать дату `sunset` и документировать альтернативы.

16.2. Должно быть получено одобрение клиентов перед отключением API

  • `ДОЛЖЕН` убедиться, что все клиенты дали согласие на дату `sunset` перед отключением API, его версии или функции.
  • `СЛЕДУЕТ` помогать клиентам с миграцией, предоставляя руководства.

16.3. Должно быть собрано согласие внешних партнеров на срок устаревания

  • Если API используется внешними партнерами, владелец API `ДОЛЖЕН` определить разумный срок поддержки API после объявления об устаревании.
  • Все внешние партнеры `ДОЛЖНЫ` выразить согласие с этим сроком.

16.4. Использование устаревшего API, запланированного к выводу из эксплуатации, должно отслеживаться

  • Владельцы API `ДОЛЖНЫ` отслеживать использование API, запланированных к выводу из эксплуатации, для мониторинга прогресса миграции и избежания неконтролируемых сбоев.

16.5. Следует добавлять заголовки Deprecation и Sunset к ответам

  • В течение фазы устаревания `СЛЕДУЕТ` добавлять заголовки `Deprecation: ` (или `true`) и `Sunset: ` к каждому ответу, затронутому устаревшим элементом.
  • Это информирует клиентов о предстоящих изменениях.
  • Пример: `Deprecation: Tue, 31 Dec 2024 23:59:59 GMT`, `Sunset: Wed, 31 Dec 2025 23:59:59 GMT`.

16.6. Следует добавлять мониторинг для заголовков Deprecation и Sunset

  • Клиенты `СЛЕДУЕТ` отслеживать эти заголовки и создавать оповещения для планирования миграционных задач.

16.7. Не следует начинать использовать устаревшие API

  • Клиенты `НЕ ДОЛЖНЫ` начинать использовать устаревшие API, версии API или функции API.

17. Операции REST

17.1. Спецификация OpenAPI должна быть опубликована для не-компонентных внутренних API

  • Все сервисные приложения `ДОЛЖНЫ` публиковать спецификации OpenAPI своих внешних API.
  • Публикация происходит путем копирования спецификации OpenAPI в зарезервированный каталог `/zalando-apis` артефакта развертывания.
  • Это обеспечивает обнаружаемость API через API Portal Zalando.

17.2. Следует отслеживать использование API

  • Владельцы API `СЛЕДУЕТ` отслеживать использование API в продакшене для получения информации о клиентах.
  • Идентификация клиентов `СЛЕДУЕТ` осуществляться путем логирования `client-id` из токена OAuth.

18. Основы Событий – Типы Событий

18.1. События должны быть определены в соответствии с общими рекомендациями API

  • События `ДОЛЖНЫ` быть согласованы с другими данными API и общими рекомендациями API (насколько это применимо).
  • Большинство разделов руководства (Общие рекомендации, Основы REST – Форматы данных, Основы REST – JSON-нагрузка, Проектирование REST – Гипермедиа) применимы к обмену данными событий.

18.2. События должны рассматриваться как часть интерфейса сервиса

  • События являются частью интерфейса сервиса с внешним миром, эквивалентной по значимости REST API сервиса.
  • Сервисы, публикующие данные, `ДОЛЖНЫ` относиться к своим событиям как к первоочередной задаче проектирования (принцип “API First”).

18.3. Схема события должна быть доступна для просмотра

  • Сервисы, публикующие данные событий, `ДОЛЖНЫ` предоставить схему события, а также определение типа события для просмотра.

18.4. События должны быть специфицированы и зарегистрированы как типы событий

  • В архитектуре Zalando события регистрируются с использованием структуры, называемой `Event Type`.
  • `Event Type` `ДОЛЖЕН` определять: категорию (общая, изменение данных), имя, аудиторию, владеющее приложение, схему полезной нагрузки, режим совместимости.
  • Пример объекта `EventType` на основе OpenAPI представлен в исходном документе.

18.5. Должно соблюдаться соглашение об именовании типов событий

  • Имена типов событий `ДОЛЖНЫ` (или `СЛЕДУЕТ`, в зависимости от аудитории) соответствовать функциональному именованию:
    • ` ::= .[.]`
    • Примеры: `transactions-order.order-cancelled`, `customer-personal-data.email-changed.v2`.
  • `СЛЕДУЕТ` использовать согласованное именование, если одна и та же сущность представлена как событием изменения данных, так и RESTful API.

18.6. Должно быть указано владение типами событий

  • Определения событий `ДОЛЖНЫ` иметь явное владение, указываемое через поле `owning_application` в `EventType`.

18.7. Режим совместимости должен быть тщательно определен

  • Владельцы типов событий `ДОЛЖНЫ` уделять внимание выбору режима совместимости в `EventType` (`compatible`, `forward`, `none`).
  • `none`: любые изменения схемы разрешены.
  • `forward`: пользователи могут читать события с последней версией схемы, используя предыдущую версию.
  • `compatible`: изменения должны быть полностью совместимы (только добавление новых опциональных свойств, запрещены другие изменения).

18.8. Схема события должна соответствовать объекту схемы OpenAPI

  • `ДОЛЖЕН` использовать `Schema Object` из OpenAPI Specification для определения схем событий.
  • `НЕ ДОЛЖЕН` использовать запрещенные ключевые слова `JSON Schema` (`additionalItems`, `contains`, `patternProperties`, `dependencies`, `propertyNames`, `const`, `not`, `oneOf`).
  • `Schema Object` изменяет `additionalProperties` и добавляет `readOnly`, `discriminator`.

18.9. Следует избегать additionalProperties в схемах типов событий

  • `СЛЕДУЕТ` избегать использования `additionalProperties` в схемах типов событий, чтобы поддерживать эволюцию схемы и обратную совместимость.
  • Публиканты, стремящиеся обеспечить совместимость, `ДОЛЖНЫ` определять новые опциональные поля и обновлять свои схемы заранее.
  • Потребители `ДОЛЖНЫ` игнорировать поля, которые они не могут обработать.

18.10. Должно использоваться семантическое версионирование схем типов событий

  • Схемы событий `ДОЛЖНЫ` версионироваться аналогично REST API (`MAJOR.MINOR.PATCH`).
  • Изменения в `compatible` или `forward` режиме приводят к `PATCH` или `MINOR` ревизии (major changes не разрешены).
  • Изменения в `none` режиме могут привести к `PATCH`, `MINOR` или `MAJOR` изменениям.

19. Основы Событий – Категории Событий

19.1. Должно быть обеспечено соответствие событий категории событий

  • `ДОЛЖЕН` обеспечить, чтобы события соответствовали предопределенной структуре категории (например, `GeneralEvent` или `DataChangeEvent`).
  • `GeneralEvent`: общая категория, с полем `metadata`.
  • `DataChangeEvent`: для изменений сущностей, с полями `metadata`, `data_op`, `data_type`, `data`.

19.2. Должны предоставляться обязательные метаданные события

  • `ДОЛЖЕН` предоставлять `eid` (идентификатор события) и `occurred_at` (технический временной штамп создания события производителем) в метаданных события.
  • Другие поля (`received_at`, `version`, `parent_eids`, `flow_id`, `partition`) могут быть обогащены посредниками.

19.3. Должны предоставляться уникальные идентификаторы событий

  • `ДОЛЖЕН` предоставлять `eid` как уникальный идентификатор для события в пределах его типа/потока.
  • Производители `ДОЛЖНЫ` использовать тот же `eid` при повторной публикации того же объекта события.
  • `eid` поддерживает потребителей событий в обнаружении и устойчивости к дубликатам.

19.4. Общие события должны использоваться для сигнализации шагов в бизнес-процессах

  • При публикации событий, представляющих шаги в бизнес-процессе, типы событий `ДОЛЖНЫ` основываться на категории `General Event`.
  • `ДОЛЖЕН` содержать специфический идентификатор процесса (bp-id) и средства для корректного упорядочивания.
  • `ДОЛЖЕН` публиковать событие надежно.

19.5. Следует обеспечивать явный порядок событий для общих событий

  • `СЛЕДУЕТ` предоставлять явную информацию о деловом порядке событий для общих событий.
  • `ДОЛЖЕН` указывать свойство `ordering_key_fields` и `СЛЕДУЕТ` `ordering_instance_ids` в определении типа события.
  • Строго монотонно возрастающие версии сущностей или последовательные счетчики `МОГУТ` использоваться для упорядочивания.

19.6. События изменения данных должны использоваться для сигнализации мутаций

  • `ДОЛЖЕН` использовать события изменения данных для сигнализации изменений экземпляров сущностей и содействия захвату измененных данных (CDC).
  • События `ДОЛЖНЫ` предоставлять полные данные сущности, включая идентификатор измененного экземпляра.
  • `ДОЛЖЕН` обеспечивать явный порядок событий для событий изменения данных.
  • `ДОЛЖЕН` публиковать события надежно.

19.7. Должен быть обеспечен явный порядок событий для событий изменения данных

  • `ДОЛЖЕН` предоставлять информацию об упорядочивании для событий изменения данных, так как она критически важна для поддержания синхронизации транзакционных наборов данных (CDC).
  • Эта информация определяет порядок создания, обновления и удаления экземпляров данных.
  • Исключение: если транзакционные данные только добавляются (append only).

19.8. Следует использовать стратегию хэш-секционирования для событий изменения данных

  • `СЛЕДУЕТ` использовать стратегию хэш-секционирования для событий изменения данных.
  • Это позволяет всем связанным событиям для сущности быть последовательно назначенными одному разделу, обеспечивая относительно упорядоченный поток событий для этой сущности.
  • Ключ секционирования `ПОЧТИ ВСЕГДА` `ДОЛЖЕН` представлять изменяемую сущность.

20. Проектирование Событий

20.1. Следует избегать записи конфиденциальных данных в событиях

  • `СЛЕДУЕТ` избегать записи конфиденциальных данных (например, персональных данных) в событиях, если это не требуется для бизнеса.
  • Конфиденциальные данные создают дополнительные обязательства по контролю доступа и соответствию нормативным требованиям, увеличивая риски защиты данных.

20.2. Должна быть устойчивость к дубликатам при потреблении событий

  • Потребители событий `ДОЛЖНЫ` быть устойчивы к дубликатам событий (несколько вхождений одного и того же объекта события).
  • Устойчивость может включать дедупликацию, основанную на `eid` и информации об упорядочивании изменения данных (CDC).
  • Источники дубликатов: повторные попытки публикации, сбои, “at-least-once” доставка.

20.3. Следует проектировать для идемпотентной обработки вне порядка

  • События, разработанные для идемпотентной обработки вне порядка, `СЛЕДУЕТ` проектировать так, чтобы они содержали достаточно информации для восстановления их исходного порядка или чтобы порядок становился неактуальным.
  • Это позволяет пропускать, задерживать или повторять обработку событий без порчи результата.

20.4. Должно быть обеспечено определение полезных бизнес-ресурсов событиями

  • События `ДОЛЖНЫ` определять полезные бизнес-ресурсы, основанные на предметной области сервиса и ее естественном жизненном цикле.
  • `СЛЕДУЕТ` определять типы событий, достаточно абстрактные/общие, чтобы быть ценными для нескольких сценариев использования.

20.5. Следует обеспечивать соответствие событий изменения данных ресурсам API

  • Представление сущности в событии изменения данных `СЛЕДУЕТ` соответствовать представлению в REST API.
  • Это уменьшает количество публикуемых структур и упрощает поддержку API.
  • Исключения: сильно отличающиеся представления от хранилища данных, агрегированные данные, результаты вычислений.

20.6. Должна поддерживаться обратная совместимость для событий

  • Изменения в событиях `ДОЛЖНЫ` быть основаны на аддитивных и обратно совместимых изменениях.
  • Обратно совместимые изменения: добавление опциональных полей, изменение порядка полей, удаление опциональных полей, удаление значений из перечисления, добавление новых значений в `x-extensible-enum`.
  • Обратно несовместимые изменения: удаление обязательных полей, изменение значений по умолчанию, изменение типов полей, добавление значений в обычное перечисление.

Приложения: Разделы остаются без изменений, так как они ссылаются на внешние ресурсы и инструментарий, которые не были частью критического анализа содержания.

  • Приложение A: Ссылки
  • Приложение B: Инструментарий
  • Приложение C: Лучшие практики
  • Приложение D: Журнал изменений (текущий Changelog из оригинального документа)

Итог:

В ходе данного анализа и обновления “Zalando RESTful API and Event Guidelines” была проведена систематическая работа по пересмотру и улучшению исходного документа. Основные цели заключались в устранении двусмысленностей, обеспечении соответствия современным тенденциям в проектировании API и повышении общей читаемости и применимости руководства.

Ключевым изменением, отражающим современные практики и обеспечивающим лучшую совместимость с широко используемыми языками программирования и инструментами, стало `РЕКОМЕНДАЦИЯ` использования `camelCase` для имен свойств в JSON-нагрузке, вместо ранее предписанного `snake_case`. Это решение было тщательно обосновано с учетом удобства разработки и консистентности с глобальными стандартами. Хотя это отступление от одной из конкретных конвенций исходного документа, оно полностью соответствует его духу “API как продукта” и направлено на улучшение опыта разработчиков.

Кроме того, были уточнены и усилены правила, касающиеся:

  • HTTP-статусов: Более детальный и выборочный подход к использованию HTTP-кодов, отдавая предпочтение наиболее распространенным и понятным.
  • Идемпотентности: Расширенное рассмотрение различных подходов к обеспечению идемпотентности для `POST` и `PATCH` операций, включая заголовки `Idempotency-Key`.
  • Безопасности: Четкие указания по работе со скоупами и защите конечных точек.
  • Версионирования: Подтверждение предпочтения версионирования через `media type` и категорический отказ от версионирования URL.
  • Обработки ошибок: Настоятельная рекомендация использовать `Problem JSON` (RFC 9457) для стандартизированного и расширяемого представления ошибок.
  • Событий: Дополнительные пояснения и уточнения по проектированию, совместимости и структуре типов событий, а также по обработке дубликатов.

Это обновленное руководство представляет собой более зрелый и всеобъемлющий документ, который, как ожидается, будет служить надежным ориентиром для проектирования и разработки высококачественных, масштабируемых и удобных в использовании API и систем событий в Zalando. Оно сохраняет основные принципы и ценности исходного документа, одновременно адаптируясь к меняющимся требованиям и лучшим практикам индустрии. Дальнейшее периодическое обновление и итеративное улучшение будут способствовать поддержанию его актуальности и эффективности.

И еще немного про безопасность в масштабе

Ranger vs. OPA: Битва архитектур... и почему OPAL меняет правила игры

● Ranger has a fixed development model
● To add new systems you need to write new modules,
compile and roll out Ranger
● OPA is all REST
● Basically everything is configuration
● We can build the 80% abstraction layer easily
● Anybody else -> they can build whatever extra they need -> in config!

В мире современных распределённых систем управление доступом (авторизация) — одна из самых сложных и критически важных задач. Компании постоянно ищут баланс между безопасностью, гибкостью и скоростью разработки. Начальные тезисы были абсолютно точны:

У Ranger фиксированная модель разработки. Чтобы добавить поддержку новых систем, нужно писать новые модули. [...] OPA полностью построен на REST. По сути, всё является конфигурацией. [...] Мы можем создать 80% абстракции, а остальные сами допишут всё, что нужно, через конфиг!

Это сравнение описывает переход от традиционной, монолитной архитектуры к современной, микросервисной. Однако история неполная без третьего ключевого элемента — OPAL, который решает фундаментальную проблему OPA при масштабировании.

Давайте рассмотрим все три компонента по порядку.

1. “Классический” подход: Apache Ranger

Apache Ranger — это зрелая и мощная система для централизованного управления политиками безопасности в экосистеме больших данных (Hadoop, Hive, Kafka и т.д.).

  • Как это работает: Ranger работает по принципу “сервер + плагины”. Центральный сервер хранит все политики доступа. В каждую защищаемую систему (например, в Hive) устанавливается специальный плагин, который периодически опрашивает сервер Ranger, скачивает актуальные политики и кэширует их для быстрой проверки доступа.
  • Сильные стороны:
    • Централизация: Единый центр для аудита и управления доступом.
    • Мощность: Глубокая интеграция с поддерживаемыми системами (например, безопасность на уровне колонок в Hive).
  • Слабые стороны (те самые “фиксированные модели разработки”):
    • Негибкость: Поддержка новой системы, не входящей в стандартный набор, требует написания плагина на Java, компиляции и развертывания новой версии Ranger. Это медленно и требует узкой экспертизы.
    • “Бутылочное горлышко”: Все изменения проходят через центральную команду, что замедляет продуктовые команды.
    • Не для микросервисов: Этот подход плохо подходит для динамичного мира микросервисов, где новые сервисы появляются каждый день.

Аналогия: Ranger — это как служба безопасности крупного завода, которая работает только со стандартными станками этого завода. Если вы покупаете новый станок из-за границы, вам нужно написать для службы безопасности целую новую инструкцию и переобучить персонал.

2. “Гибкий” подход: Open Policy Agent (OPA)

OPA — это универсальный движок политик с открытым исходным кодом. Его философия прямо противоположна Ranger. OPA ничего не знает о тех, кого он защищает.

  • Как это работает:
    1. Ваш сервис, получив запрос, формирует JSON-документ с контекстом (`{“user”: “alice”, “action”: “read”, “resource”: “document”}`).
    2. Он отправляет этот JSON в OPA через простой REST API-вызов.
    3. OPA применяет к этому JSON’у правила, написанные на языке Rego, и мгновенно возвращает решение: `allow` или `deny`.
  • Сильные стороны:
    • Универсальность: OPA может управлять доступом к чему угодно — микросервисам, Kubernetes, конвейерам CI/CD, базам данных.
    • Policy-as-Code: Политики на Rego — это код. Их можно хранить в Git, версионировать, тестировать и автоматически развертывать.
    • Децентрализация: OPA обычно развертывается как “сайдкар”-контейнер рядом с каждым экземпляром сервиса, что обеспечивает низкую задержку и высокую отказоустойчивость.
  • Проблема, которую OPA создает:
    Представьте, у вас 500 микросервисов, и рядом с каждым работает свой экземпляр OPA. Возникают вопросы:
    • Как доставить обновление политики во все 500 экземпляров OPA одновременно?
    • Откуда OPA возьмет данные для принятия решений (например, список ролей пользователя или владельцев документа)? Если каждый из 500 экземпляров OPA будет сам ходить в базу данных, это создаст колоссальную нагрузку.

Здесь на сцену выходит OPAL.

3. “Связующее звено”: OPAL (Open Policy Administration Layer)

OPAL — это не еще один движок политик. Это административный слой реального времени для OPA. Его единственная задача — поддерживать политики и данные в ваших OPA-агентах в актуальном состоянии.

  • Как это работает (OPA + OPAL):**
    1. Политики (Rego-файлы) хранятся в Git-репозитории. Данные (роли, атрибуты) — в базах данных или API.
    2. OPAL Server подписывается на изменения в этих источниках (например, через веб-хуки из Git или топики Kafka).
    3. Когда происходит изменение (например, разработчик пушит новую политику в Git), OPAL Server получает уведомление.
    4. Сервер немедленно публикует сообщение об обновлении в легковесный канал (pub/sub, обычно через WebSockets).
    5. OPAL Clients, работающие рядом с каждым OPA, получают это сообщение.
    6. Клиенты сами скачивают нужные обновления (новую политику из Git, свежие данные из БД) и загружают их в свой локальный OPA.
  • Что это дает:
    • Обновления в реальном времени: Изменение политики в Git моментально распространяется по всей системе.
    • Событийная архитектура: Нет необходимости постоянно опрашивать источники. Это очень эффективно.
    • Полное разделение: OPA отвечает только за принятие решений. OPAL — за доставку “знаний” для этих решений.
    • Масштабируемость: Эта архитектура легко управляет тысячами OPA-агентов, решая проблему синхронизации.
    • Завершение истории GitOps: Вы управляете доступом ко всей вашей инфраструктуре через `git push`, что полностью соответствует исходному тезису: “всё является конфигурацией”. medium.com

Итоговое сравнение

Критерий Apache Ranger OPA (самостоятельно) OPA + OPAL (Современный стек)
Архитектура Монолитный сервер + плагины Децентрализованный движок политик Децентрализованный движок + слой управления реального времени
Процесс обновления Код -> Компиляция -> Развертывание Ручная загрузка политик через API `git push` -> Автоматическое распространение
Гибкость Низкая (только для поддерживаемых систем) Очень высокая (универсальный) Очень высокая + управляемость в масштабе
Управление данными Встроено Требует самостоятельного решения Встроено в архитектуру (OPAL следит за данными)
Масштабируемость Масштабируется, но обновления медленные Плохо масштабируется с точки зрения управления Отлично масштабируется
Подход Классический, централизованный `Policy-as-Code`, но неполный `Policy-as-Code` + `GitOps`, событийно-ориентированный

Вывод

Возвращаясь к исходным тезисам, становится ясно, что их автор описывал потенциал OPA. Однако чтобы этот потенциал раскрылся в крупной организации, необходима система, которая возьмет на себя рутинную, но критически важную работу по синхронизации.

  • Ranger — это мощный, но неповоротливый инструмент из прошлого, идеальный для статичных, гомогенных сред.
  • OPA — это гениально простой и гибкий движок, сердце современной авторизации.
  • OPAL — это нервная система, которая соединяет это сердце с “мозгом” (Git, базы данных) и позволяет всему организму (вашим микросервисам) реагировать на изменения мгновенно.

Современный, масштабируемый и по-настоящему гибкий “слой абстракции”, о котором говорилось в начале, строится именно на связке OPA + OPAL. Это позволяет создавать платформу, ценность которой, как и было сказано, “заключается в способности объединять внешние инструменты, команды, данные и процессы”.

Еще было это: https://gavrilov.info/all/evolyuciya-upravleniya-dostupom-opa-opal-vs-fga-rbac-rebac/

Платформа

Оригинал:

A platform is a set of software and a surrounding ecosystem of resources that helps you to grow your business. A platform enables growth through connection: its value comes not only from its own features, but from its ability to connect external tools, teams, data, and processes.

Перевод:

Платформа — это комплекс программных решений и окружающая их экосистема ресурсов, которые способствуют росту вашего бизнеса. Платформа стимулирует рост за счёт связности: её ценность заключается не только в собственных функциях, но и в способности объединять внешние инструменты, команды, данные и процессы.

Дополнение с учетом современных трендов и опыта

Приведённая цитата — отличное базовое определение. Однако современное понимание платформ стало значительно шире и глубже. Сегодня платформа — это не просто технологический фундамент, а ключевой стратегический актив для трансформации всего бизнеса.

Вот несколько ключевых аспектов, дополняющих это определение:

  1. Платформа как основа бизнес-стратегии.
    Современные лидеры рынка рассматривают платформы не как набор инструментов, а как основу для полного переосмысления своего бизнеса. Платформенный подход позволяет заново выстроить всю цепочку поставок, клиентский путь и процессы принятия решений. Это критический компонент «цифрового ядра» компании, стремящейся к постоянному обновлению и росту accenture.com https://www.accenture.com/us-en/insights/strategy/platform-strategy.
  1. Ценность требует инвестиций и настройки.
    Платформа сама по себе не является готовым решением. Она требует значительных инвестиций в настройку и интеграцию для того, чтобы начать генерировать ценность. Как отмечает Forrester, пустой аккаунт в `Amazon Web Services` (AWS) бесполезен до тех пор, пока вы не установите и не настроите на нём программное обеспечение и не окружите его дополнительными возможностями forrester.com https://www.forrester.com/blogs/a-simple-definition-of-platform.
  1. Переход от монолита к экосистеме.
    Эпоха единых монолитных IT-приложений, которые должны были решать все задачи бизнеса, подходит к концу. Современная парадигма — это создание гибкой архитектуры, в которой решения от разных поставщиков могут быть бесшовно развёрнуты и интегрированы. Такие «инновационные платформы» позволяют компаниям быстро адаптироваться к изменениям и использовать лучшие в своём классе инструменты для каждой функции cimdata.com https://www.cimdata.com/en/platformization-some-common-definitions.
  1. Драйвер новых бизнес-моделей и конкурентного преимущества.
    Платформенные бизнес-модели — это проверенный способ достижения успеха. Они позволяют технологическим компаниям монетизировать свои продукты через модели `as-a-service` (по подписке или на основе потребления), повышать лояльность клиентов за счёт связанных сервисов и масштабироваться гораздо быстрее. Для 80% компаний, ещё не перешедших на платформенную модель, главным вызовом становится растущая конкуренция со стороны тех, кто уже сделал этот шаг ey.com https://www.ey.com/en_nz/insights/technology/how-can-your-platform-business-model-fuel-competitiveness-and-growth.
  1. Создание сетевого эффекта.
    Ключевая особенность успешной платформы — способность создавать сетевой эффект. Чем больше пользователей, разработчиков, партнёров и сервисов подключается к платформе, тем выше становится её ценность для каждого участника. Примерами могут служить операционные системы (`Windows`, `macOS`), платформы для разработки (`Java`), браузеры (`Chrome`) или социальные сети, где ценность напрямую зависит от количества и активности пользователей и создателей контента appmarketplace.com https://appmarketplace.com/blog/what-is-a-platform.

Итоговое современное определение можно сформулировать так:

Платформа — это стратегический бизнес-актив, представляющий собой гибкую и масштабируемую технологическую основу, которая объединяет внутренние и внешние ресурсы (инструменты, данные, команды, партнёров) в единую экосистему. Её главная цель — не просто предоставлять функции, а стимулировать рост, инновации и создание сетевых эффектов, позволяя компании переосмысливать бизнес-модели и получать устойчивое конкурентное преимущество.

Новая эра трансформации данных: dbt против Bruin и aaC

В мире данных произошла тихая, но фундаментальная революция. На смену традиционному подходу ETL (Extract, Transform, Load), где данные преобразовывались до загрузки в хранилище, пришла новая парадигма — ELT (Extract, Load, Transform). Благодаря мощности современных облачных хранилищ (таких как Snowflake, BigQuery, Databricks, Starburst\Trino) стало выгоднее сначала загружать сырые данные, а уже затем трансформировать их непосредственно в хранилище.

Этот сдвиг породил потребность в инструментах, которые специализируются на последнем шаге — трансформации (T). Именно в этой нише dbt (data build tool) стал безоговорочным лидером, но на его поле появляются и новые сильные игроки, такие как Bruin. Давайте разберемся, что это за инструменты, какой подход они олицетворяют и в чем их ключевые различия.

Подход «Аналитика как код»

И dbt, и Bruin являются яркими представителями движения “Analytics as Code” (аналитика как код). Это не просто инструменты, а целая философия, которая переносит лучшие практики разработки программного обеспечения в мир аналитики данных.

Основные принципы и идеи:

  1. Версионирование: Все трансформации данных описываются в виде кода (в основном SQL), который хранится в системе контроля версий, такой как Git. Это позволяет отслеживать изменения, совместно работать и откатываться к предыдущим версиям.
  2. Модульность и переиспользование (DRY – Don’t Repeat Yourself): Сложные трансформации разбиваются на небольшие, логически завершенные модели, которые могут ссылаться друг на друга. Это делает код чище, понятнее и позволяет повторно использовать уже написанную логику.
  3. Тестирование: Код трансформаций должен быть протестирован. Инструменты позволяют автоматически проверять качество данных после преобразований: на уникальность ключей, отсутствие `NULL` значений, соответствие заданным условиям и т.д.
  4. Документация и прозрачность: Процесс трансформации становится самодокументируемым. Инструменты могут автоматически генерировать документацию и строить графы зависимостей моделей (data lineage), показывая, как данные текут и преобразуются от источника к конечному виду. element61.be
  5. CI/CD (Continuous Integration / Continuous Deployment): Изменения в коде трансформаций могут автоматически тестироваться и разворачиваться в продуктивную среду, что значительно ускоряет циклы разработки.

Решаемые проблемы:

  • “Черные ящики” ETL: Заменяют сложные, трудноподдерживаемые и непрозрачные ETL-процессы на понятный и документированный код.
  • Рассинхронизация команд: Стирают границы между инженерами данных и аналитиками, позволяя аналитикам, владеющим SQL, самостоятельно создавать надежные модели данных.
  • Низкое качество данных: Встроенные механизмы тестирования помогают обеспечить надежность и согласованность данных.

---

dbt (data build tool): Золотой стандарт трансформации

dbt — это инструмент с открытым исходным кодом, который позволяет аналитикам и инженерам трансформировать данные в их хранилищах с помощью простых SQL-запросов. Важно понимать, что dbt не извлекает и не загружает данные. Он специализируется исключительно на шаге “T” в ELT vutr.substack.com. dbt git.

Он работает как компилятор и исполнитель: вы пишете модели данных в `.sql` файлах, используя шаблонизатор Jinja для добавления логики (циклы, условия, макросы). Затем dbt компилирует этот код в чистый SQL и выполняет его в вашем хранилище данных element61.be.

Плюсы dbt
  • Огромное сообщество и экосистема: dbt стал де-факто стандартом индустрии. Существует огромное количество статей, курсов, готовых пакетов (библиотек) и экспертов.
  • Фокус на SQL: Низкий порог входа для аналитиков, которые уже знают SQL. Это демократизирует процесс трансформации данных.
  • Мощное тестирование и документирование: Встроенные команды для тестирования данных и автоматической генерации проектной документации с графом зависимостей.
  • Зрелость и надежность: Инструмент проверен временем и используется тысячами компаний по всему миру.
  • Гибкость: Благодаря шаблонизатору Jinja можно создавать очень сложные и переиспользуемые макросы, адаптируя dbt под любые нужды.
Минусы dbt
  • Только трансформация: dbt не занимается извлечением (E) и загрузкой (L). Для этого вам понадобятся отдельные инструменты (например, Fivetran, Airbyte), что усложняет стек технологий.
  • Кривая обучения: Хотя основы просты, освоение продвинутых возможностей Jinja, макросов и структуры проекта требует времени.
  • Зависимость от Python-моделей: Хотя недавно появилась поддержка моделей на Python, она все еще не так нативна и проста, как основной SQL-подход, и требует дополнительных настроек.

---

Bruin Data: Универсальный боец

Bruin — это более новый игрок на рынке, который позиционирует себя как инструмент для создания “end-to-end” пайплайнов данных. В отличие от dbt, он не ограничивается только трансформацией, а стремится охватить больше этапов работы с данными, включая их загрузку (ingestion) https://github.com/bruin-data/bruin.

Bruin разделяет ту же философию “Analytics as Code”, но предлагает более интегрированный опыт, где SQL и Python являются равноправными гражданами.

Плюсы Bruin
  • Универсальность: Один инструмент для определения всего пайплайна: от загрузки из источников до финальных витрин данных. Это может упростить стек технологий.
  • Нативная поддержка SQL и Python: Позволяет легко комбинировать задачи на разных языках в одном пайплайне без дополнительных настроек. Это идеально для задач, где чистый SQL громоздок (например, работа с API, машинное обучение).
  • Простота конфигурации: Зачастую требует меньше шаблонного кода (boilerplate) для определения ассетов и пайплайнов по сравнению с dbt.
  • Встроенное качество данных: Как и dbt, делает акцент на проверках качества на каждом шаге.
Минусы Bruin
  • Пока маленькое сообщество: Как у нового инструмента, у Bruin гораздо меньше пользователей, готовых решений и обсуждений на форумах по сравнению с dbt. Найти помощь или готовый пакет для решения специфической задачи сложнее.
  • Незрелость: Инструмент моложе, а значит, наверное, потенциально менее стабилен и может иметь меньше интеграций по сравнению с проверенным dbt. Пока нет облачных функция за деньги. Я так думал, но все же есть https://getbruin.com.
  • “Мастер на все руки — эксперт ни в чем?”: Стремление охватить все этапы (E, L, T) может означать, что в каждом отдельном компоненте Bruin может уступать лучшим в своем классе специализированным инструментам (например, Fivetran в загрузке, dbt в трансформации), но это конечно субъективно.

Сводное сравнение

Характеристика dbt (data build tool) Bruin Data
Основная задача Трансформация (T в ELT) Весь пайплайн (E, L, T)
Ключевые языки SQL с шаблонизатором Jinja SQL и Python как равноправные
Экосистема Огромная, стандарт индустрии Маленькая, развивающаяся
Зрелость Высокая, проверен временем Низкая/Средняя
Стек инструментов Требует отдельных E/L инструментов Стремится быть самодостаточным

Итого

Выбор между dbt и Bruin — это выбор между двумя стратегиями построения современного стека данных.

Выбирайте dbt, если:

  • Вы строите гибкий стек из лучших в своем классе инструментов (“best-of-breed”): один для загрузки, другой для хранения, третий для трансформации.
  • Ваша команда в основном состоит из аналитиков, сильных в SQL.
  • Для вас критически важны поддержка сообщества, стабильность и наличие готовых решений.
  • Вы работаете в большой организации, где принятие отраслевых стандартов является преимуществом.
  • Вы готовы переехать к ним в платное облако, когда нибудь. Большая часть функционала доступна там.

Выбирайте Bruin, если:

  • Вы предпочитаете единый, интегрированный инструмент для управления всеми пайплайнами, чтобы упростить архитектуру
  • Вы любите open source и End-to-end дата framework: фор data ingestion + transformations + кволити. :)
  • Ваши пайплайны требуют тесной связки SQL и Python для трансформаций (например, обогащение данных через вызовы API или модели ML).
  • Вы начинаете новый проект или работаете в небольшой команде и цените скорость настройки и меньшее количество движущихся частей.
  • Вы Go’шник :) – Bruin написан на Go почти на 100%.

И dbt, и Bruin — мощные инструменты, воплощающие современные подходы к инженерии данных. dbt предлагает проверенный, сфокусированный и невероятно мощный движок для трансформаций, ставший стандартом. Bruin же предлагает более универсальный и интегрированный подход, который может быть привлекателен для команд, стремящихся к простоте и нативной поддержке Python.

А что такое “Аналитика как код” (Analytics as Code, AaC)?

Аналитика как код — это подход к управлению аналитическими процессами, при котором все компоненты аналитики — от моделей данных и метрик до отчетов и правил доступа — определяются в виде кода в человекочитаемых файлах. Эти файлы затем управляются так же, как исходный код любого другого программного обеспечения: с помощью систем контроля версий, автоматизированного тестирования и развертывания medium.com.

Самая близкая и известная аналогия — это Infrastructure as Code (IaC). Как IaC (например, с помощью Terraform) позволил инженерам описывать серверы, сети и базы данных в коде вместо ручной настройки через веб-интерфейсы, так и AaC позволяет описывать в коде всё, что связано с данными medium.com.

Идея проста и убедительна: “настройте свои системы один раз, выразите это в виде кода, а затем поместите в систему контроля версий” holistics.io.

Проблема: Как было раньше?

Чтобы понять ценность AaC, нужно посмотреть на проблемы, которые он решает. В традиционном подходе аналитика часто была разрозненной и хрупкой:

  • Логика в “черных ящиках”: Сложные преобразования данных были скрыты внутри GUI-интерфейсов старых ETL-инструментов или непосредственно в настройках BI-платформы (например, Tableau, Power BI). Никто, кроме автора, не мог легко понять, как рассчитывается та или иная метрика.
  • Разрозненные SQL-скрипты: Аналитики хранили важные SQL-запросы на своих локальных машинах, в общих папках или на wiki-страницах. Не было единой версии правды, код дублировался и быстро устаревал.
  • Отсутствие контроля версий: Невозможно было отследить, кто, когда и почему изменил логику расчета ключевого показателя. Откат к предыдущей работающей версии был настоящей головной болью.
  • “Ручное” тестирование: Проверка качества данных после изменений была ручным, подверженным ошибкам процессом. Часто о проблемах узнавали уже от бизнес-пользователей, которые видели неверные цифры в отчетах.
  • Рассинхронизация: Инженеры данных готовили сырые таблицы, а аналитики строили свою логику поверх них. Любые изменения с одной стороны могли сломать всю цепочку, не будучи замеченными вовремя.

Этот хаос приводил к главному — недоверию к данным. Никто не мог быть уверен, что цифры в дашборде верны.

Ключевые принципы “Аналитики как код”

AaC решает эти проблемы, внедряя практики из мира разработки ПО.

  1. Декларативное определение: Все аналитические артефакты описываются в файлах.
    • Модели данных:** `SELECT * FROM ...` в `.sql` файлах.
    • Тесты:** `not_null`, `unique` в `.yml` файлах.
    • Документация:** Описания таблиц и полей в `.yml` файлах.
    • Метрики и дашборды:** Определения в `.yml` или специализированных файлах medium.com.
  1. Контроль версий (Git): Весь код хранится в репозитории (например, на GitHub или GitLab).
    • Прозрачность:** Каждое изменение — это `commit` с понятным описанием.
    • Совместная работа:** Аналитики работают в отдельных ветках, а изменения вносятся через `Pull Request` (или `Merge Request`), что позволяет проводить ревью кода (code review).
    • Восстанавливаемость:** Если что-то пошло не так, можно легко откатиться к предыдущей версии.
  1. Автоматизированное тестирование: Тесты являются неотъемлемой частью кода. Они запускаются автоматически при каждом изменении, чтобы гарантировать, что данные по-прежнему соответствуют ожиданиям (например, `user_id` всегда уникален и не равен `NULL`).
  1. CI/CD (Непрерывная интеграция и развертывание): Процессы полностью автоматизированы.
    • Когда аналитик вносит изменения в `Pull Request`, автоматически запускаются тесты.
    • После одобрения и слияния ветки изменения автоматически развертываются в продуктивной среде (например, dbt Cloud или Jenkins запускает команду `dbt run`).
  1. Модульность и переиспользование (DRY – Don’t Repeat Yourself): Сложные потоки данных разбиваются на небольшие, логичные и переиспользуемые модели. Одна модель может ссылаться на другую, создавая четкий граф зависимостей (lineage), который можно визуализировать.

Преимущества подхода AaC

Принятие этой философии дает компании ощутимые выгоды:

  • Надежность и доверие: Благодаря автоматическому тестированию и ревью кода значительно повышается качество данных, а вместе с ним и доверие бизнеса к аналитике.
  • Скорость и гибкость: Аналитики могут вносить изменения гораздо быстрее. Цикл от идеи до готового отчета сокращается с недель до дней или даже часов.
  • Масштабируемость: Кодовая база легко поддерживается и расширяется. Новые члены команды могут быстро разобраться в проекте благодаря документации и прозрачности.
  • Прозрачность и обнаруживаемость: Автоматически сгенерированная документация и графы зависимостей позволяют любому сотруднику понять, откуда берутся данные и как они рассчитываются.
  • Демократизация: AaC дает возможность аналитикам, владеющим SQL, самостоятельно создавать надежные и протестированные модели данных, не дожидаясь инженеров данных. Это стирает барьеры между командами.

В конечном итоге, “Аналитика как код” — это культурный сдвиг, который превращает аналитику из ремесленного занятия в зрелую инженерную дисциплину, обеспечивая скорость, надежность и масштабируемость, необходимые современному бизнесу.

Эволюция управления доступом: OPA\OPAL vs FGA, RBAC, ReBAC

В разработке программного обеспечения управление доступом пользователей — одна из критически важных задач. От того, кто и какие действия может выполнять в приложении, напрямую зависит его безопасность, функциональность и надежность. Исторически логика авторизации часто была разбросана по всему коду приложения, что приводило к появлению архитектурного антипаттерна, известного как «Большой ком грязи» (Big Ball of Mud).

Что такое «Большой ком грязи»? Это система, в которой отсутствует четкая архитектура. Логика авторизации, выраженная в бесконечных `if-else` конструкциях, смешивается с бизнес-логикой, обработкой данных и представлением. Такую систему практически невозможно поддерживать, аудировать и масштабировать. Любое изменение в правах доступа требует переписывания кода в разных местах, что увеличивает риск ошибок и уязвимостей.

Современный подход заключается в отделении логики авторизации от основного кода приложения с помощью специализированных инструментов — механизмов политик (Policy Engines). Эти системы позволяют централизованно определять, управлять и применять правила доступа. В предоставленном материале рассматриваются три ведущих механизма: OPA (Open Policy Agent), OpenFGA и AWS Cedar.

Основной подход: Политика как код vs. Политика как данные

Ключевое различие между механизмами политик заключается в их подходе к определению правил. Это разделение можно описать как «управляемые политикой» (policy-driven) и «управляемые данными» data-driven https://www.permit.io/blog/policy-engine-showdown-opa-vs-openfga-vs-cedar.

  1. Управляемые политикой (Policy-Driven)
    В этой модели основная логика авторизации описывается в виде кода на специальном декларативном языке. Данные, такие как атрибуты пользователя или ресурса, передаются в механизм во время запроса для принятия решения.
  • Cedar (AWS): Яркий пример такого подхода. Cedar делает акцент на читаемости и безопасности политик. Политики пишутся так, чтобы их было легко понять и верифицировать. Joy Scharmen из StrongDM отмечает: «Cedar очень ориентирован на политики. Данные проходят через систему как эфемерный ввод, не требуя предопределенной модели данных» https://www.permit.io/blog/policy-engine-showdown-opa-vs-openfga-vs-cedar. Это идеально для систем, где важна предсказуемость и простота аудита.
  1. Управляемые данными (Data-Driven)
    Здесь ядром системы являются данные, описывающие *отношения* между субъектами (пользователями) и объектами (ресурсами). Политика — это, по сути, модель, которая интерпретирует эти отношения.
  • OpenFGA (на основе Google Zanzibar): Этот механизм реализует модель управления доступом на основе отношений (ReBAC). Вы определяете модель (например, «владелец документа может его удалить»), а конкретные связи («пользователь `Alice` является владельцем `document:123`») хранятся как данные https://www.permit.io/blog/opa-cedar-openfga-why-are-policy-languages-trending. Этот подход чрезвычайно масштабируем для систем со сложными иерархиями и связями, как в Google Docs или социальных сетях.
  1. Гибридный подход
    Некоторые механизмы поддерживают оба подхода.
  • OPA (Open Policy Agent): OPA является универсальным механизмом общего назначения https://www.permit.io/blog/policy-engines. Он позволяет как загружать данные и политики в виде «пакетов» (bundles), так и получать их динамически во время выполнения. Это дает максимальную гибкость, но требует более тщательного проектирования архитектуры.
Архитектурные модели развертывания

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

  • Централизованная модель: Все сервисы обращаются к единому центральному сервису авторизации.
    • Плюсы: Единый источник правды, консистентность решений.
    • Минусы: Может стать узким местом (bottleneck) и единой точкой отказа.
  • Децентрализованная модель: Механизм политик разворачивается как «сайдкар» (sidecar) рядом с каждым экземпляром приложения.
    • Плюсы: Минимальная задержка (latency), высокая отказоустойчивость.
    • Минусы: Требует синхронизации политик и данных между всеми экземплярами.
  • Гибридная модель: «Управляй централизованно, авторизуй локально». Политики и данные управляются из центра, но доставляются на децентрализованные механизмы для локального принятия решений.

Для решения проблемы синхронизации в децентрализованных моделях существуют инструменты, такие как OPAL (Open Policy Administration Layer). OPAL работает поверх OPA, Cedar и OpenFGA, обнаруживая изменения в политиках и данных в реальном времени и доставляя обновления агентам https://docs.opal.ac.

Сравнение механизмов: плюсы и минусы
Механизм Описание Плюсы Минусы
:--- :--- :--- :---
OPA (Open Policy Agent) Универсальный механизм общего назначения, использующий язык Rego. Гибкость: Поддерживает RBAC, ABAC, ReBAC. Может использоваться не только для авторизации.
Экосистема: Зрелый проект CNCF с огромным сообществом и инструментарием.
Адаптивность: Может работать в stateful и stateless режимах, быть централизованным или децентрализованным.
Сложность: Язык Rego имеет порог вхождения.
Требует дисциплины: Гибкость может привести к усложнению, если не планировать архитектуру тщательно.
OpenFGA Специализированный механизм, основанный на Google Zanzibar. Реализует ReBAC. Масштабируемость: Идеален для систем с большим количеством пользователей и сложными отношениями между объектами.
Производительность: Проверенная модель, оптимизированная для быстрых проверок разрешений.
Четкая модель: Фокусируется на одной задаче и делает ее хорошо.
Узкая специализация: Менее интуитивен для простых сценариев RBAC или ABAC https://www.permit.io/blog/opa-cedar-openfga-why-are-policy-languages-trending
Синхронизация данных: Основная сложность — поддерживать граф отношений в актуальном состоянии.
AWS Cedar Механизм, ориентированный на безопасность, читаемость и формальную верификацию политик. Простота и читаемость: Политики легко писать и аудировать.
Безопасность: Встроенные средства верификации для проверки корректности политик.
Низкий порог вхождения: Интуитивно понятен для команд, новых в этой области.
Ограниченная гибкость: В первую очередь предназначен для авторизации и менее гибок, чем OPA.
Stateless-ориентированность: Модель, где все данные передаются с запросом, может не подходить для всех сценариев.
Итог: нет “победителя”, есть правильный инструмент

Как было подчеркнуто в дискуссии на KubeCon, «победителя нет». Выбор механизма политик полностью зависит от конкретного случая использования:

  • Если у вас сложная система с множеством взаимосвязанных разрешений (например, совместное редактирование документов, социальная сеть), OpenFGA — ваш выбор.
  • Если вам нужен универсальный инструмент для управления политиками в разных частях стека (Kubernetes, CI/CD, микросервисы) и ваша команда готова изучить новый язык, OPA предоставит максимальную гибкость.
  • Если ваша главная цель — простая, безопасная и легко проверяемая авторизация в приложении, и вы цените читаемость политик, Cedar будет отличным стартом.

Переход от «большого кома грязи» к внешним механизмам политик — это шаг к созданию более надежных, безопасных и поддерживаемых систем. Благодаря таким проектам, как OPA, OpenFGA, Cedar и инструментам вроде OPAL, разработчики получают мощные средства для построения современных систем управления доступом https://www.permit.io/blog/introduction-to-opal

OPA + OPAL == 💜

https://github.com/permitio/opal?tab=readme-ov-file

Спойлер ...

Apache SeaTunnel – Движение к мультимодальной интеграции данных

Новое позиционирование Apache SeaTunnel. Движение к унифицированному инструменту для мультимодальной интеграции данных

Введение

В постоянно меняющемся мире больших данных эффективная и надежная интеграция данных является ключевым фактором для успеха любого предприятия. Apache SeaTunnel (ранее известный как Waterdrop) зарекомендовал себя как мощный инструмент для синхронизации данных. Однако с развитием технологий и появлением новых вызовов, таких как интеграция разнородных типов данных (структурированных, полуструктурированных и неструктурированных), проект пересматривает свое позиционирование. Цель — превратиться из простого инструмента синхронизации в комплексную, унифицированную платформу для мультимодальной интеграции данных.

Проблемы предыдущей архитектуры

Изначально Apache SeaTunnel был разработан как плагин, работающий поверх вычислительных движков, таких как Apache Spark и Apache Flink. Такой подход имел свои преимущества, позволяя использовать мощность этих движков, но также порождал ряд проблем:

  1. Зависимость от сторонних движков: Для выполнения даже самых простых задач по пересылке данных требовалось развертывание и поддержка тяжеловесных кластеров Spark или Flink. Это увеличивало накладные расходы, усложняло настройку и повышало порог входа для новых пользователей.
  2. Сложность конфигурации: Пользователям приходилось разбираться не только в конфигурации самого SeaTunnel, но и в настройках Spark/Flink, что часто приводило к так называемому “конфигурационному аду”.
  3. Ограничения коннекторов: Разработка коннекторов была тесно связана с API Spark и Flink, что затрудняло создание универсальных коннекторов, работающих в обеих средах без изменений.
  4. Низкая производительность для простых задач: Использование мощных, но громоздких движков для элементарных задач ETL (Extract, Transform, Load) было избыточным и неэффективным с точки зрения ресурсов и времени запуска.

Новое видение: унифицированная платформа с собственным движком

Чтобы решить эти проблемы и соответствовать современным требованиям, сообщество Apache SeaTunnel представило новую архитектуру, в основе которой лежит собственный вычислительный движок — SeaTunnel Engine.

Этот стратегический шаг позволил отделить SeaTunnel от обязательной зависимости от Spark и Flink. Теперь SeaTunnel может работать в самостоятельном режиме, что обеспечивает следующие ключевые преимущества:

  • Легковесность и быстрота: `SeaTunnel Engine` специально оптимизирован для задач интеграции данных. Он запускается быстрее и потребляет значительно меньше ресурсов, чем полноценные кластеры Spark или Flink, что делает его идеальным для широкого круга задач.
  • Унификация пакетной и потоковой обработки: Новая архитектура изначально спроектирована для бесшовной работы как с пакетными (batch), так и с потоковыми (streaming) данными. Пользователям больше не нужно поддерживать два разных стека для разных типов задач — SeaTunnel предоставляет единый интерфейс и модель выполнения.
  • Упрощенная разработка коннекторов: С введением унифицированного API коннекторов (`Connector API`), разработчикам стало проще создавать новые интеграции. Коннектор, написанный для `SeaTunnel Engine`, будет работать одинаково для всех сценариев, что ускоряет расширение экосистемы.

Мультимодальная интеграция данных

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

  1. Структурированные данные: Традиционная область для SeaTunnel. Поддерживается множество реляционных баз данных (MySQL, PostgreSQL), аналитических СУБД (ClickHouse, Doris) и хранилищ данных.
  2. Полуструктурированные данные: Эффективная работа с NoSQL базами данных (MongoDB, Elasticsearch) и потоками событий (Kafka, Pulsar).
  3. Неструктурированные данные: Расширение поддержки для озер данных (Data Lakes) и файловых систем (HDFS, S3, OSS). Это включает интеграцию с форматами вроде Apache Hudi, Iceberg и Delta Lake.

Особое внимание уделяется критически важным функциям, таким как Захват изменяемых данных (CDC) и синхронизация всей базы данных целиком. SeaTunnel теперь может считывать журналы транзакций (например, binlog в MySQL) для захвата изменений в реальном времени и применять их к целевой системе. Функция полной синхронизации позволяет в одной задаче перенести схему и все данные из одной базы в другую, что значительно упрощает миграцию.

Будущее развитие

Дорожная карта проекта включает в себя:

  • Расширение экосистемы коннекторов: Добавление поддержки еще большего числа источников и приемников, включая современные SaaS-платформы и векторные базы данных для задач ИИ.
  • Улучшенная поддержка озер данных: Углубление интеграции с форматами Hudi и Iceberg, поддержка эволюции схем и транзакционных операций.
  • Пользовательский интерфейс: Разработка визуального интерфейса для создания и мониторинга заданий, что сделает инструмент более доступным для широкого круга пользователей.
  • Повышение производительности и стабильности: Непрерывная оптимизация `SeaTunnel Engine` для еще более быстрой и надежной обработки данных.

Заключение

Apache SeaTunnel совершает важный переход от зависимого инструмента к самостоятельной, легковесной и унифицированной платформе для интеграции данных. Отказ от обязательной привязки к Spark/Flink и внедрение собственного `SeaTunnel Engine` открывают новые возможности для пользователей, которым нужно простое, но мощное решение для пакетной и потоковой обработки разнородных данных. Новое позиционирование делает SeaTunnel сильным конкурентом в мире современных ETL/ELT инструментов.

---

Выводы

Проанализировав направление развитие Apache SeaTunnel, можно сделать несколько ключевых выводов:

  1. Стратегическая зрелость: Переход на собственный движок (`SeaTunnel Engine`) — это признак зрелости проекта. Команда осознала, что зависимость от универсальных, но тяжеловесных движков (Spark/Flink) является узким местом для основного сценария использования — интеграции данных. Создание специализированного движка позволяет оптимизировать производительность и снизить накладные расходы именно для этих задач.
  2. Соответствие трендам: Этот шаг полностью соответствует общему тренду в индустрии данных — движению от монолитных, “умеющих все” платформ к более легковесным и специализированным инструментам. Для многих задач по перемещению и простой трансформации данных запуск Spark-кластера является избыточным. SeaTunnel теперь предлагает “золотую середину”.
  3. Конкурентное позиционирование:
    • Против коммерческих SaaS ETL (Fivetran, Airbyte): SeaTunnel является мощной open-source альтернативой. Он привлекателен для компаний, которые хотят полного контроля над своей инфраструктурой, стремятся избежать зависимости от поставщика (vendor lock-in) и имеют техническую экспертизу для самостоятельного развертывания и поддержки.
    • Против специализированных CDC-инструментов (Debezium): SeaTunnel не просто захватывает изменения (CDC), а встраивает эту функциональность в полноценный конвейер интеграции. Это решение “все в одном”, которое позволяет не только извлечь данные, но и доставить их в целевую систему (например, озеро данных или хранилище) в рамках одного инструмента.
  4. Фокус на “мультимодальности” — это задел на будущее. Поддержка не только реляционных баз и Kafka, но и озер данных (Hudi, Iceberg) и, в перспективе, векторных баз, говорит о том, что проект нацелен на обслуживание современных стеков данных, включая аналитику в реальном времени и конвейеры для машинного обучения (MLOps).
Рекомендации

Исходя из этого, можно дать следующие рекомендации:

  1. Кому стоит обратить внимание на Apache SeaTunnel:
    • Командам, для которых Spark/Flink избыточны. Если ваша основная задача — это синхронизация данных между различными источниками (например, из MySQL в ClickHouse или из Kafka в HDFS) без сложных вычислений, `SeaTunnel Engine` может оказаться значительно более эффективным и простым в эксплуатации решением.
    • Компаниям, ищущим open-source замену коммерческим ETL-инструментам. Если у вас есть экспертиза для управления Java-приложениями и вы хотите построить гибкую, масштабируемую и экономичную платформу интеграции данных, SeaTunnel — отличный кандидат.
    • Пользователям экосистемы Apache. Проект тесно интегрируется с другими популярными проектами Apache (Doris, Hudi, Flink, Spark), что делает его естественным выбором для тех, кто уже использует эти технологии.
    • Инженерам, которым нужна унификация. Если вы устали поддерживать отдельные скрипты или инструменты для пакетной и потоковой обработки, SeaTunnel предлагает единый подход к обоим сценариям.
  1. Что нужно проверить перед внедрением:
    • Экосистему коннекторов: Самое важное — убедиться, что в SeaTunnel есть готовые, стабильные коннекторы для всех ваших источников и приемников данных. Хотя сообщество активно их добавляет, покрытие может быть не таким широким, как у коммерческих лидеров рынка.
    • Функциональность CDC: Если вам нужен захват изменений в реальном времени, детально изучите поддержку вашей СУБД. Проверьте, насколько стабильно работает коннектор и какие гарантии доставки (exactly-once, at-least-once) он предоставляет.
    • Операционная сложность: Несмотря на то, что SeaTunnel стал проще, это все еще open-source инструмент, требующий мониторинга, настройки и периодических обновлений. Убедитесь, что у вашей команды есть ресурсы для его поддержки.

Apache SeaTunnel трансформируется в мощный и современный инструмент, который заслуживает внимания со стороны инженеров данных. Его новое позиционирование как легковесной, унифицированной платформы делает его сильным игроком на поле интеграции данных.

iceberg-kafka-connect

Крутой блог по всей экостистеме кафка, примеры по iceberg которые разобраны ниже

https://rmoff.net/2025/07/04/writing-to-apache-iceberg-on-s3-using-kafka-connect-with-glue-catalog/

небольшой пост про CDC от автора книги гроккаем конкурентность

https://luminousmen.com/post/change-data-capture

дока по iceberg-sink connector

https://github.com/databricks/iceberg-kafka-connect

kafka vizualizer

https://softwaremill.com/kafka-visualisation/

А тут видосик:

Описание патерна Slowly Changing Dimensions (SCD)

Slowly Changing Dimensions (SCD), или Медленно меняющиеся измерения, — это концепция и набор методов из области хранилищ данных (Data Warehousing), которые используются для управления изменениями в атрибутах измерений с течением времени. Измерения — это справочные таблицы, которые описывают бизнес-сущности, такие как клиенты, продукты, сотрудники, географические регионы.

Атрибуты этих сущностей (например, адрес клиента или категория продукта) меняются, но обычно не очень часто — отсюда и название “медленно меняющиеся”. Основная задача SCD — решить, как хранить эти изменения, чтобы обеспечить точность исторических отчетов www.datacamp.com.

Например, если вы просто перезапишете адрес клиента, вы потеряете информацию о том, где он жил раньше. Это может исказить анализ продаж по регионам за прошлые периоды. Патерны SCD предлагают различные стратегии для решения этой проблемы.

Основные типы SCD

Существует несколько типов SCD, но самыми распространенными и фундаментальными являются Типы 1, 2 и 3.

---

Тип 1: Перезапись атрибута (Overwrite)

Это самый простой подход. При изменении атрибута старое значение просто перезаписывается новым.

  • Как работает:** Находится существующая запись в таблице измерения и значение в нужном столбце обновляется.
  • Когда использовать:** Когда нет необходимости хранить историю изменений. Например, для исправления опечатки в имени клиента.
  • Преимущества:** Простота реализации, не требует увеличения объема хранилища.
  • Недостатки: **История изменений полностью теряется. Анализ, основанный на исторических значениях атрибута, становится невозможным.

Пример:
У нас есть клиент Анна Петрова, которая живет в Москве.

*Таблица `DimCustomer` до изменений:*

CustomerKey FullName City
:--- :--- :---
101 Анна Петрова Москва

Анна переезжает в Санкт-Петербург. При использовании SCD Тип 1 таблица будет обновлена:

*Таблица `DimCustomer` после изменений:*

CustomerKey FullName City
:--- :--- :---
101 Анна Петрова Санкт-Петербург

Теперь невозможно узнать, что раньше Анна жила в Москве.

---

Тип 2: Добавление новой строки (Add New Row)

Это самый распространенный и мощный тип SCD, так как он позволяет сохранять полную историю изменений.

  • Как работает:** Вместо перезаписи существующей записи, создается новая запись для той же сущности (например, того же клиента). Старая запись помечается как неактуальная (истекшая), а новая — как актуальная. Для этого в таблицу измерения обычно добавляют несколько служебных столбцов learn.microsoft.com:
    • `StartDate` / `EffectiveDate` — дата, с которой запись стала актуальной.
    • `EndDate` — дата, когда запись перестала быть актуальной.
    • `IsCurrent` / `CurrentFlag` — флаг (например, ‘Yes’/’No’ или 1/0), показывающий, является ли эта запись текущей.
  • Когда использовать:** Когда сохранение истории критически важно для анализа. Это стандартный выбор для большинства атрибутов в хранилищах данных.
  • Преимущества:** Сохраняется полная, точная история. Позволяет проводить корректный point-in-time анализ (анализ на определенный момент времени).
  • Недостатки:** Увеличивается объем таблицы, так как для одного клиента может быть несколько записей. Запросы могут стать сложнее (нужно фильтровать по флагу `IsCurrent` или по диапазону дат) hevodata.com.

Пример:
Снова используем пример с Анной Петровой.

*Таблица `DimCustomer` до изменений:*

SurrogateKey CustomerID FullName City StartDate EndDate IsCurrent
:--- :--- :--- :--- :--- :--- :---
1 101 Анна Петрова Москва 2020-01-15 NULL Yes

Анна переезжает 16 августа 2024 года. При использовании SCD Тип 2 таблица изменится так:

*Таблица `DimCustomer` после изменений:*

SurrogateKey CustomerID FullName City StartDate EndDate IsCurrent
:--- :--- :--- :--- :--- :--- :---
1 101 Анна Петрова Москва 2020-01-15 2024-08-15 No
2 101 Анна Петрова Санкт-Петербург 2024-08-16 NULL Yes

Теперь мы сохранили всю историю перемещений Анны.

---

Тип 3: Добавление нового атрибута (Add New Attribute)

Этот тип сохраняет ограниченную историю, добавляя в таблицу отдельный столбец для предыдущего значения атрибута.

  • Как работает:** Создается новый столбец, например, `PreviousCity`. Когда атрибут `City` меняется, его старое значение копируется в `PreviousCity`, а новое записывается в `City`.
  • Когда использовать:** Когда важно отслеживать только предыдущее состояние для сравнения, а более глубокая история не нужна.
  • Преимущества:** Простота реализации, не увеличивает количество строк, легко запрашивать текущее и предыдущее значения.
  • Недостатки:** Сохраняет историю только на один шаг назад. Не масштабируется, если нужно хранить более двух-трех последних значений.

Пример:
Анна переезжает из Москвы в Санкт-Петербург.

*Таблица `DimCustomer` до изменений:*

CustomerKey FullName CurrentCity PreviousCity
:--- :--- :--- :---
101 Анна Петрова Москва NULL

*Таблица `DimCustomer` после изменений:*

CustomerKey FullName CurrentCity PreviousCity
:--- :--- :--- :---
101 Анна Петрова Санкт-Петербург Москва

Если Анна переедет снова, значение “Москва” будет потеряно.

Другие типы SCD

Существуют и более сложные гибридные типы:

  • Тип 4 (History Table):** Основная таблица измерения хранит только текущие данные (как Тип 1), а вся история изменений выносится в отдельную таблицу. Это полезно, когда изменения происходят часто в очень больших таблицах измерений medium.com.
  • Тип 6 (Hybrid):** Комбинирует подходы Типов 1, 2 и 3. Например, в таблице хранятся поля для полной истории (SCD2) и одновременно поле для текущего значения (SCD1 для быстрого доступа) и предыдущего значения (SCD3 для сравнения).

Тип 4: Добавление исторической таблицы (History Table / Audit Table)

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

  • Как работает:** Создаются две таблицы:
    1. Таблица измерения (Dimension Table): Хранит *только* текущие, самые последние данные. Эта таблица по своей сути работает как SCD Тип 1 (данные просто перезаписываются). Она маленькая, быстрая и идеально подходит для большинства запросов, где история не нужна.
    2. Историческая таблица (History Table): Хранит всю историю изменений. Каждый раз, когда в основной таблице происходит изменение, старая версия строки (до обновления) добавляется в историческую таблицу. Эта таблица часто содержит служебные поля, как в SCD Тип 2 (`StartDate`, `EndDate`, `Version`), для отслеживания временного периода.
  • Когда использовать:** Когда у вас есть очень большая таблица измерений (например, десятки миллионов клиентов), и большинство аналитических запросов относится только к текущим данным. Разделение таблиц позволяет сделать эти частые запросы очень быстрыми, не жертвуя при этом возможностью проводить глубокий исторический анализ при необходимости.
  • Преимущества:**
    • Высокая производительность для запросов к текущим данным.
    • Логическое разделение данных: актуальные и исторические.
  • Недостатки:**
    • Усложнение ETL/ELT процесса, так как нужно управлять двумя таблицами.
    • Анализ, требующий одновременного доступа к историческим и текущим данным, усложняется, так как требует `JOIN` или `UNION` между двумя таблицами.

Пример:
Клиент Анна Петрова переезжает из Москвы в Санкт-Петербург.

*Таблицы до изменений:*

`DimCustomer` (основная таблица)

CustomerID FullName City
:--- :--- :---
101 Анна Петрова Москва

`HistoryCustomer` (историческая таблица) – *пустая*

*Процесс изменения:*

  1. Перед обновлением основной таблицы, текущая строка (Анна в Москве) копируется в `HistoryCustomer`.
  2. Затем основная таблица `DimCustomer` обновляется новым значением.

*Таблицы после изменений:*

`DimCustomer` (всегда хранит только актуальные данные)

CustomerID FullName City
:--- :--- :---
101 Анна Петрова Санкт-Петербург

`HistoryCustomer` (накапливает историю)

HistoryID CustomerID FullName City StartDate EndDate
:--- :--- :--- :--- :--- :---
1 101 Анна Петрова Москва 2020-01-15 2024-08-15

Тип 5: Гибридный подход (Mini-Dimension + Type 1 Outrigger)

Идея: Вынести часто меняющиеся атрибуты из большой таблицы измерений в отдельную “мини-таблицу”, чтобы избежать “раздувания” основной таблицы.

  • Как работает:**
    1. Из основной таблицы измерения (например, `DimCustomer`) выделяется группа атрибутов, которые часто меняются вместе (например, “Тарифный план”, “Статус подписки”).
    2. Создается отдельная таблица — “мини-измерение” (например, `DimSubscriptionProfile`) — только для этих атрибутов. Эта мини-таблица управляется по SCD Тип 2 (добавление новой строки для каждого уникального набора значений).
    3. В основной таблице `DimCustomer` эти атрибуты удаляются, и вместо них добавляется один внешний ключ (например, `SubscriptionProfileKey`), который ссылается на мини-измерение.
    4. Этот ключ в основной таблице `DimCustomer` обновляется по принципу SCD Тип 1 (просто перезаписывается), указывая на *актуальную* запись в мини-измерении.
  • Когда использовать:** В очень больших (широких и/или с большим количеством строк) таблицах измерений, где лишь небольшая группа атрибутов меняется относительно часто. Это позволяет отслеживать историю этих атрибутов, не создавая новую многомиллионную запись в основной таблице при каждом изменении.
  • Преимущества:**
    • Экономия места и контроль над ростом основной таблицы измерения.
    • Позволяет вести детальную историю для подгруппы атрибутов.
  • Недостатки:**
    • Более сложная модель данных, требующая дополнительных `JOIN`.
    • Может быть сложнее для понимания конечными пользователями.

Пример:
Клиент Иван меняет свой тарифный план.

*Таблицы до изменений:*

`DimCustomer`

CustomerKey FullName SubscriptionProfileKey
:--- :--- :---
202 Иван Иванов 55

`DimSubscriptionProfile` (мини-измерение, управляется по SCD2)

ProfileKey Plan Status IsCurrent
:--- :--- :--- :---
55 Basic Active Yes

*Процесс изменения:* Иван переходит на план “Premium”.

  1. В `DimSubscriptionProfile` добавляется новая строка для “Premium”, а старая помечается как неактуальная.
  2. В `DimCustomer` у Ивана обновляется ключ `SubscriptionProfileKey`.

*Таблицы после изменений:*

`DimCustomer` (здесь изменился только ключ)

CustomerKey FullName SubscriptionProfileKey
:--- :--- :---
202 Иван Иванов 56

`DimSubscriptionProfile` (здесь хранится вся история)

ProfileKey Plan Status IsCurrent
:--- :--- :--- :---
55 Basic Active No
56 Premium Active Yes

Тип 6: Гибридный (Комбинация Типа 1, 2 и 3)

Идея: Обеспечить максимальную гибкость для анализа, объединив сильные стороны трех основных типов в одной таблице.

  • Как работает: Этот тип строится на основе **SCD Тип 2 (добавление новой строки для истории), но с добавлением атрибутов из SCD Тип 1 (перезапись) для упрощения некоторых запросов.
    • Основная структура — это SCD Тип 2: есть строки для каждой исторической версии с полями `StartDate`, `EndDate` и `IsCurrent`. Поле атрибута (например, `City`) хранит значение, актуальное на тот исторический период.
    • Дополнительно в таблицу добавляется столбец `CurrentCity`. Этот столбец для *всех* записей одного клиента (и исторических, и текущей) всегда хранит актуальное на данный момент значение (поведение SCD Тип 1).
  • Когда использовать:** Когда аналитикам часто нужно отвечать на два типа вопросов:
    1. “Каким был город клиента на момент продажи?” (Используется историческое поле `City`).
    2. “Каковы продажи всем клиентам, которые *сейчас* живут в Москве, за всю историю?” (Используется поле `CurrentCity` для фильтрации).
  • Преимущества:**
    • Невероятная гибкость анализа без сложных `JOIN` или подзапросов для определения текущего состояния.
  • Недостатки:**
    • Усложнение ETL/ELT. При изменении адреса нужно не только создать новую строку и закрыть старую, но и обновить поле `CurrentCity` во всех предыдущих строках для этого клиента. Это может быть ресурсозатратно.

Пример:
Снова Анна, переезжающая из Москвы в Санкт-Петербург.

*Таблица `DimCustomer` до изменений:*

SurrogateKey CustomerID City CurrentCity StartDate EndDate IsCurrent
:--- :--- :--- :--- :--- :--- :---
1 101 Москва Москва 2020-01-15 NULL Yes

*Процесс изменения:*

  1. Старая строка “закрывается” (обновляется `EndDate`, `IsCurrent` = ‘No’).
  2. Создается новая актуальная строка.
  3. Во всех строках для CustomerID=101 поле `CurrentCity` обновляется до “Санкт-Петербург”.

*Таблица `DimCustomer` после изменений:*

SurrogateKey CustomerID City CurrentCity StartDate EndDate IsCurrent
:--- :--- :--- :--- :--- :--- :---
1 101 Москва Санкт-Петербург 2020-01-15 2024-08-15 No
2 101 Санкт-Петербург Санкт-Петербург 2024-08-16 NULL Yes

Теперь можно легко отфильтровать по `City` для исторического анализа или по `CurrentCity` для анализа в разрезе текущего состояния.

Ссылки для дальнейшего изучения

Идея: Концептуальная архитектура: SCD на стеке Lakehouse + Data Mesh + dbt

Основная идея заключается в создании надежных, версионируемых и децентрализованных “продуктов данных”, одним из которых является таблица измерений с полной историей (SCD). (Автоматическая)

Вот как компоненты взаимодействуют друг с другом:

  1. Lakehouse (Основа): Это наша физическая среда. Мы используем открытое озеро данных (например, S3, ADLS) для хранения, а поверх него — табличный формат Apache Iceberg. Iceberg предоставляет нам ACID-транзакции, эволюцию схемы и, что самое важное для SCD, атомарные и эффективные операции `MERGE` (`UPDATE`/`INSERT`/`DELETE`) на уровне строк прямо в озере данных.
  1. Data Mesh (Философия организации): Вместо централизованной команды данных, мы принимаем философию Data Mesh. A “Команда домена Клиенты” несет полную ответственность за все данные, связанные с клиентами. Их задача — предоставить остальной компании высококачественный продукт данных под названием `dim_customers`. Этот продукт должен включать полную историю изменений (SCD Type 2).
  1. ETL/ELT (Процесс): Это конвейер, по которому данные текут от источника к потребителю.
    • Extract & Load: Исходные данные (например, изменения в базе данных клиентов) захватываются с помощью CDC (Change Data Capture) инструментов типа Debezium и попадают в **Kafka. Оттуда они загружаются (Load) в “бронзовый” слой нашего Lakehouse (в сыром виде, в таблицы Iceberg).
    • Transform: Здесь в игру вступает **dbt. Команда домена использует `dbt` для преобразования сырых данных из бронзового слоя в готовую к использованию модель в “серебряном” слое — нашу таблицу `dim_customers`.
  1. dbt (Инструмент автоматизации SCD): `dbt` является сердцем автоматизации. Он не просто выполняет SQL-скрипты. У него есть встроенный функционал для реализации SCD Type 2, который называется `Snapshots`.

---

Сценарий 1: Автоматическое формирование SCD с помощью `dbt snapshots`

Это наиболее распространенный, надежный и идиоматический способ реализации идеи.

Как это работает:

  1. Источник: У нас есть “бронзовая” таблица `bronze_customers`, которая содержит текущее состояние всех клиентов. Эта таблица обновляется периодически (например, раз в час) новыми данными из Kafka.
  2. dbt Snapshot: В проекте `dbt` команда домена создает файл “снэпшота” (`snapshot/customers_snapshot.sql`). Внутри него описывается, как `dbt` должен отслеживать изменения.
{% snapshot customers_snapshot %}

    {{
        config(
          target_schema='silver',
          unique_key='customer_id',
          strategy='check',
          check_cols=['address', 'email', 'phone_number'],
          updated_at='last_modified_at',
        )
    }}

    select * from {{ source('bronze', 'customers') }}

    {% endsnapshot %}
  1. Автоматизация: Оркестратор (например, Airflow) запускает команду `dbt snapshot` по расписанию.
  2. Что делает dbt “под капотом”:
    • Он сравнивает записи из исходной таблицы (`bronze_customers`) с текущими записями в целевой таблице (`silver.customers_snapshot`).
    • Используя `unique_key` (`customer_id`), он находит совпадающие записи.
    • С помощью стратегии `check` он проверяет, изменилось ли значение в любом из столбцов, перечисленных в `check_cols`.
    • Если изменение обнаружено:
      • Он обновляет старую запись в целевой таблице, проставляя ей дату окончания актуальности (`dbt_valid_to`).
      • Он вставляет новую строку с обновленными данными и датой начала актуальности (`dbt_valid_from`).
    • `dbt` генерирует одну атомарную операцию `MERGE` для таблицы Iceberg, которая эффективно выполняет все эти обновления и вставки за одну транзакцию.

Результат: В `silver.customers_snapshot` мы получаем идеальную таблицу SCD Type 2, которая обновляется автоматически и надежно, без написания сложной логики `MERGE` вручную.

Earlier Ctrl + ↓