Работа с мета-данными

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


Структура метаданных на уровне suite

Валидационный набор в Vest может быть расширен объектом метаданных, который привязывается к самому набору. Это позволяет описывать общий контекст: форму, сущность данных, режим проверки или окружение.

import { create, test, enforce } from 'vest';

const suite = create('user_form', (data = {}) => {
  test('username', 'invalid username', () => {
    enforce(data.username).isNotEmpty();
  });
}, {
  meta: {
    form: 'registration',
    version: 2,
    domain: 'auth'
  }
});

Метаданные на уровне suite обычно используются для:

  • идентификации типа данных (форма, сущность, модуль)
  • версионирования правил валидации
  • классификации доменной области
  • интеграции с внешними системами аналитики или логирования

Метаданные на уровне тестов

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

test(
  'email',
  'email is invalid',
  () => {
    enforce(data.email).isEmail();
  },
  {
    meta: {
      severity: 'high',
      code: 'EMAIL_INVALID',
      category: 'format'
    }
  }
);

Типовые сценарии применения метаданных тестов:

  • классификация ошибок по уровню критичности
  • привязка к кодам ошибок API
  • группировка правил по типу проверки (format, security, business)
  • интеграция с UI-слоями (например, отображение tooltip по severity)

Доступ к метаданным внутри выполнения suite

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

import { get, meta } from 'vest';

const suite = create('profile', (data) => {
  const mode = meta().get('mode');

  if (mode === 'strict') {
    test('age', 'too young', () => {
      enforce(data.age).greaterThan(18);
    });
  } else {
    test('age', () => {
      enforce(data.age).greaterThan(16);
    });
  }
});

Использование runtime-метаданных применяется в случаях:

  • переключения режимов валидации (strict / relaxed)
  • A/B тестирования правил
  • адаптации под разные клиентские платформы
  • динамического включения/выключения проверок

Изменение метаданных во время выполнения

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

import { meta } from 'vest';

meta().set('mode', 'strict');
meta().set('locale', 'ru-RU');

Подобный подход используется для:

  • передачи контекста запроса (например, язык интерфейса)
  • конфигурации окружения (production, staging)
  • хранения вспомогательных флагов исполнения

Наследование и объединение метаданных

Метаданные suite и тестов не конфликтуют, а существуют в разных слоях. При необходимости они объединяются на уровне логики приложения или внешнего обработчика.

Пример комбинирования:

const suite = create('order', (data) => {
  test(
    'price',
    'invalid price',
    () => {
      enforce(data.price).greaterThan(0);
    },
    {
      meta: {
        field: 'price',
        severity: 'critical'
      }
    }
  );
}, {
  meta: {
    module: 'checkout',
    domain: 'ecommerce'
  }
});

Такое разделение позволяет:

  • отделять глобальные характеристики от локальных
  • избегать дублирования информации
  • строить многоуровневую систему классификации ошибок

Использование метаданных для обработки результатов

Результаты выполнения suite могут обогащаться метаданными для последующей обработки в UI или API-слое. Это позволяет строить структурированные ответы, где каждая ошибка несёт не только сообщение, но и контекст.

const result = suite(data);

result.getErrors().forEach(error => {
  const meta = error.meta;

  if (meta.severity === 'critical') {
    // обработка критических ошибок
  }
});

Применение на практике:

  • визуальная дифференциация ошибок по цветам и приоритету
  • фильтрация ошибок в интерфейсе
  • генерация структурированных API-ответов
  • логирование с расширенными атрибутами

Метаданные как часть архитектуры валидации

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

Основные архитектурные сценарии:

  • построение централизованного каталога ошибок
  • связывание валидации с бизнес-доменами
  • автоматическая генерация документации по правилам
  • синхронизация фронтенда и бэкенда через единый набор кодов ошибок

Контекстные метаданные и интеграция с внешними системами

Метаданные часто используются как мост между валидацией и внешними системами:

  • системы мониторинга
  • аналитические платформы
  • логирование ошибок
  • UI-рендеринг ошибок

Пример расширенного использования:

test(
  'password',
  'weak password',
  () => {
    enforce(data.password).longerThan(8);
  },
  {
    meta: {
      analytics: {
        event: 'validation_error',
        field: 'password'
      },
      ui: {
        highlight: true,
        tooltip: 'Password too weak'
      }
    }
  }
);

Ограничения и принципы проектирования метаданных

При проектировании системы метаданных в Vest важно учитывать ряд архитектурных ограничений:

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

Рациональная модель предполагает:

  • минимально необходимый набор ключей
  • строгую типизацию (при использовании TypeScript)
  • единые соглашения по именованию полей

Динамическое расширение метаданных

Метаданные могут формироваться не только статически, но и динамически, исходя из входных данных или внешнего контекста.

test('username', 'invalid', () => {
  enforce(data.username).matches(/^[a-z]+$/);
}, {
  meta: {
    generatedAt: Date.now(),
    requestId: data.requestId
  }
});

Это позволяет:

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

Метаданные как слой абстракции над правилами

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