Принцип работы декораторов валидации

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

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


Этапы работы декораторов

Процесс работы можно разделить на два фундаментальных этапа:

1. Фаза определения классов

При объявлении класса и применении декораторов происходит регистрация правил:

  • анализируется декоратор (например, @IsString(), @MinLength(5))

  • формируется описание ограничения

  • описание сохраняется в глобальном хранилище метаданных

  • связь устанавливается между:

    • классом
    • свойством
    • типом проверки
    • параметрами ограничения

Важно: на этом этапе не создаётся ни одного объекта класса, а значит никакой проверки данных не происходит.


2. Фаза выполнения валидации

Когда вызывается функция валидации (validate, validateSync), происходит обратный процесс:

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

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


Роль reflect-metadata

В основе механизма лежит библиотека reflect-metadata, которая позволяет:

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

Каждый декоратор записывает информацию через ключи метаданных, например:

  • тип валидатора
  • ограничения (минимум, максимум, регулярное выражение)
  • параметры преобразования

Метаданные аккумулируются в структуру, доступную глобальному хранилищу валидатора.


Структура хранения правил

Class-validator использует централизованное хранилище метаданных, где информация организована примерно следующим образом:

  • класс

    • свойство

      • список валидаторов

        • тип проверки
        • опции
        • контекст (группы, условия выполнения)

Такой подход позволяет:

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

Как декораторы связываются с полями класса

При использовании декоратора вида:

@IsString()
name: string;

происходит следующее:

  • декоратор вызывается при определении класса
  • получает target (прототип класса) и имя свойства
  • записывает правило в хранилище

В результате свойство name получает ассоциированный набор ограничений, но само по себе не изменяется.


Принцип ленивой интерпретации правил

Одним из ключевых архитектурных решений является ленивая интерпретация метаданных:

  • декораторы только регистрируют правила
  • выполнение правил откладывается до вызова validate
  • логика проверки отделена от описания модели

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

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

Встроенные валидаторы как декораторы

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

  • @IsString() — проверяет тип string
  • @IsNumber() — проверяет числовой тип
  • @MinLength(n) — проверяет длину строки
  • @Max(n) — ограничивает числовое значение

Внутри каждый декоратор:

  • регистрирует тип проверки
  • сохраняет функцию-валидатор или ссылку на неё
  • фиксирует параметры

Фактическое выполнение происходит в общем движке валидации.


Пользовательские декораторы

Библиотека позволяет создавать собственные декораторы через механизм регистрации:

  • создаётся функция-валидатор
  • определяется логика проверки
  • декоратор связывает её с метаданными

Пример логической структуры:

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

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


Связь декораторов с ValidationMetadataStorage

Центральным компонентом выступает хранилище метаданных, которое:

  • регистрирует все правила из декораторов
  • группирует их по классам
  • предоставляет API для извлечения правил
  • используется при запуске validate

Каждый декоратор при вызове добавляет запись в это хранилище, а не изменяет объект напрямую.


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

Дополнительный уровень логики позволяет управлять применением правил:

  • validation groups
  • conditional validation
  • зависимые поля

Декораторы могут быть привязаны к определённым группам, что позволяет:

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

Порядок применения нескольких декораторов

Если к одному свойству применено несколько декораторов, они:

  • регистрируются последовательно
  • накапливаются в массив правил
  • применяются при валидации в порядке добавления

Это означает, что итоговая проверка формируется как композиция независимых ограничений.


Разделение ответственности между декораторами и валидатором

Архитектура строго разделяет две зоны:

Декораторы:

  • описывают правила
  • сохраняют метаданные
  • не выполняют проверку

Валидатор:

  • интерпретирует метаданные
  • выполняет проверки
  • формирует ошибки

Такое разделение делает систему расширяемой и предсказуемой.


Влияние TypeScript на механизм декораторов

Хотя библиотека используется в JavaScript-окружении, её основная сила раскрывается через TypeScript:

  • доступ к типам через reflect-metadata
  • автоматическое определение типов свойств
  • усиление статической структуры моделей

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


Ограничения и особенности выполнения

Работа декораторов имеет несколько важных особенностей:

  • выполняются один раз при загрузке модуля
  • не зависят от экземпляров классов
  • требуют включённого experimentalDecorators
  • зависят от корректной настройки reflect-metadata

Это означает, что вся система строится вокруг этапа инициализации приложения, а не runtime-логики объектов.