В Class-validator каждый класс рассматривается как набор правил, применяемых к его свойствам через декораторы. Композиция валидаторов возникает уже на уровне простого перечисления нескольких декораторов над одним полем.
Каждый декоратор добавляет независимое правило в общую цепочку проверки свойства. При валидации все правила выполняются последовательно, а результат формируется как агрегированная совокупность ошибок.
import { IsString, Length, IsNotEmpty } from 'class-validator';
class UserDto {
@IsString()
@IsNotEmpty()
@Length(3, 20)
username;
}
В данном примере свойство username одновременно проходит
несколько независимых проверок:
Композиция здесь реализуется не через объединение функций, а через стек метаданных, который формируется библиотекой.
Важной особенностью является то, что порядок декораторов не всегда влияет на порядок выполнения правил. Class-validator собирает метаданные, а затем запускает пайплайн валидации.
@IsNotEmpty()
@Length(5, 10)
@IsString()
field;
Несмотря на визуальный порядок, выполнение происходит как единый набор правил. Результатом становится массив ошибок, каждая из которых содержит:
Композиция валидаторов таким образом ориентирована не на последовательное прерывание, а на полное накопление ошибок.
Каждый декоратор можно рассматривать как функцию-валидатор, зарегистрированную в системе метаданных. При объединении нескольких декораторов формируется логическое пересечение условий (AND-логика).
@IsString()
@IsEmail()
email;
Значение считается валидным только при выполнении всех условий одновременно.
Одним из мощных механизмов композиции выступают группы. Они позволяют разделять наборы правил по сценариям использования одной и той же модели.
import { IsEmail, IsOptional } from 'class-validator';
class UserDto {
@IsEmail({}, { groups: ['create'] })
email;
@IsEmail({}, { groups: ['update'] })
@IsOptional()
email;
}
Группы позволяют формировать различные комбинации валидаторов без изменения структуры класса. Валидация становится контекстно-зависимой.
Механика групп реализует композицию на уровне сценариев:
Условные валидаторы позволяют включать или исключать правила в зависимости от состояния объекта.
import { ValidateIf, IsString } from 'class-validator';
class UserDto {
@ValidateIf(o => o.isActive === true)
@IsString()
nickname;
isActive;
}
Здесь формируется динамическая композиция:
Таким образом создаётся ленивое объединение правил, зависящее от данных объекта.
Сложные структуры данных требуют вложенной валидации. Class-validator
поддерживает рекурсивную композицию через
ValidateNested.
import { ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
class Address {
@IsString()
city;
}
class UserDto {
@ValidateNested()
@Type(() => Address)
address;
}
Здесь композиция становится иерархической:
Такая структура формирует дерево валидаторов, где каждый узел содержит собственный набор правил.
Расширение системы выполняется через создание кастомных декораторов с
registerDecorator.
import { registerDecorator } from 'class-validator';
function IsEven() {
return function (object, propertyName) {
registerDecorator({
name: 'isEven',
target: object.constructor,
propertyName,
validator: {
validate(value) {
return typeof value === 'number' && value % 2 === 0;
}
}
});
};
}
Кастомные валидаторы интегрируются в общую систему композиции наравне со встроенными.
При использовании нескольких кастомных и встроенных правил формируется единый пайплайн проверки.
Более сложные композиции строятся через объединение логических условий внутри одного валидатора.
import { ValidateIf, Min, Max } from 'class-validator';
class Product {
@ValidateIf(o => o.price !== null)
@Min(10)
@Max(1000)
price;
}
Здесь наблюдается композиция двух уровней:
Такая схема позволяет строить многоуровневые правила без усложнения структуры класса.
Хотя Class-validator не занимается трансформацией напрямую, в связке с class-transformer возникает комбинированная модель обработки:
import { Type } from 'class-transformer';
import { IsInt, Min } from 'class-validator';
class Order {
@Type(() => Number)
@IsInt()
@Min(1)
quantity;
}
Композиция здесь включает два этапа:
Таким образом формируется конвейер обработки данных.
Композиция валидаторов становится наиболее эффективной при разделении ответственности между декораторами:
@IsString()
@Length(5, 50)
@IsOptional()
title;
Каждый элемент решает строго ограниченную задачу, что позволяет легко комбинировать их в любых сочетаниях.
Часто возникает необходимость повторного использования одинаковых наборов правил. Это достигается через создание составных декораторов.
import { applyDecorators } from '@nestjs/common';
import { IsString, Length } from 'class-validator';
function IsShortText() {
return applyDecorators(
IsString(),
Length(3, 30)
);
}
Такой подход формирует абстракцию над набором правил, превращая их в единый логический блок.
Композиция в этом случае переносится на уровень архитектуры приложения.
При наложении множества правил возможны ситуации пересечения логики. Class-validator не разрешает конфликты автоматически, а просто агрегирует ошибки.
@Min(10)
@Max(5)
value;
В этом случае оба условия могут быть проверены независимо, и результатом станет набор противоречивых ошибок.
Композиция здесь не включает механизм разрешения конфликтов, что перекладывает ответственность на проектирование схемы валидаторов.
Внутренняя модель Class-validator основана на метаданных, где каждый класс преобразуется в структуру:
класс
свойство
список валидаторов
Такая структура позволяет:
При работе со сложными DTO композиция валидаторов формирует многоуровневые схемы:
ValidateNestedclass CartItem {
@IsString()
id;
@Min(1)
quantity;
}
class Cart {
@ValidateNested({ each: true })
@Type(() => CartItem)
items;
}
Здесь формируется каскадная композиция на уровне коллекций объектов.
Композиция валидаторов в Class-validator представляет собой сочетание нескольких механизмов:
Все эти элементы формируют единый декларативный подход к описанию правил валидации, где сложность распределяется между независимыми, слабо связанными компонентами.