Использование групп для различных сценариев

Механизм групп в библиотеке class-validator позволяет разделять правила валидации по контекстам использования одной и той же модели данных. Это особенно важно в ситуациях, где структура данных едина, но требования к обязательности и допустимости значений различаются в зависимости от операции.

Группы определяются строковыми идентификаторами и применяются как на уровне декораторов, так и на уровне запуска процесса валидации.


Базовый принцип работы групп

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

import { IsString, IsEmail, IsOptional } from 'class-validator';

class UserDto {
  @IsString({ groups: ['create'] })
  name: string;

  @IsEmail()
  email: string;

  @IsOptional({ groups: ['update'] })
  password?: string;
}

В данном примере:

  • name проверяется только при создании
  • email проверяется всегда (принадлежит default-группе)
  • password становится опциональным при обновлении

Передача групп при запуске валидации

Группы активируются через параметры функций validate или validateOrReject.

import { validate } from 'class-validator';

const dto = new UserDto();

validate(dto, {
  groups: ['create'],
});

При указании группы create будут активированы только те правила, которые явно с ней связаны, а также правила без указания группы (default).


Разделение сценариев Create и Update

Наиболее распространённый паттерн использования групп — разделение логики создания и обновления сущности.

import { IsString, IsOptional, IsInt } from 'class-validator';

class ProductDto {
  @IsString({ groups: ['create'] })
  title: string;

  @IsString({ groups: ['create', 'update'] })
  description: string;

  @IsInt({ groups: ['update'] })
  id: number;

  @IsOptional({ groups: ['update'] })
  slug?: string;
}

Поведение:

  • create: проверяются title и description
  • update: проверяются description, id, slug

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


Комбинирование нескольких групп

Один класс может поддерживать несколько независимых сценариев.

class OrderDto {
  @IsString({ groups: ['draft', 'submit'] })
  comment: string;

  @IsInt({ groups: ['submit'] })
  paymentId: number;

  @IsOptional({ groups: ['draft'] })
  tempNote?: string;
}

Запуск с несколькими группами:

validate(order, {
  groups: ['draft', 'submit'],
});

Если активируются сразу две группы, правила объединяются логически через объединение множеств.


Поведение default-группы

Правила без указания groups относятся к default-группе и активируются всегда, если не используется строгое разделение.

class AccountDto {
  @IsEmail()
  email: string;

  @IsString({ groups: ['admin'] })
  role: string;
}

Здесь:

  • email проверяется всегда
  • role — только при группе admin

Ограничение условий через ValidateIf и группы

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

import { ValidateIf, IsString } from 'class-validator';

class ProfileDto {
  @ValidateIf((obj, value) => obj.type === 'full')
  @IsString()
  biography: string;

  @IsString({ groups: ['full'] })
  details: string;
}

Комбинация подходов позволяет:

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

Наследование и группы в DTO

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

import { ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';

class AddressDto {
  @IsString({ groups: ['create'] })
  city: string;
}

class UserDto {
  @ValidateNested()
  @Type(() => AddressDto)
  address: AddressDto;
}
validate(user, { groups: ['create'] });

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


Динамическое переключение сценариев

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

validate(data, { groups: ['registration'] });
validate(data, { groups: ['profile_update'] });
validate(data, { groups: ['admin_override'] });

Каждая группа может представлять отдельный сценарий:

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

Смешивание обязательных и групповых правил

В реальных моделях часто комбинируются строгие и контекстные ограничения.

class SessionDto {
  @IsString()
  token: string;

  @IsOptional({ groups: ['refresh'] })
  refreshToken?: string;

  @IsString({ groups: ['login'] })
  password: string;
}

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

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

Особенности поведения при отсутствии групп

Если при валидации не указана опция groups, используются только:

  • правила без группы
  • правила с пустым массивом групп (если заданы явно как общие)
validate(dto); // активируется default-поведение

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


Группы и архитектура DTO

Использование групп позволяет отказаться от множества отдельных классов DTO и заменить их одной моделью с вариативными правилами.

Типовая структура:

  • create
  • update
  • list
  • filter
  • admin

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


Управление сложными схемами валидации

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

  • централизовать правила
  • избегать дублирования классов
  • управлять поведением через параметры вызова
validate(payload, {
  groups: ['admin', 'update'],
});

В таких случаях активируется пересечение правил нескольких сценариев, формируя итоговую схему проверки на лету.