Работа с валидацией в крупных JavaScript-проектах требует строгой структуры, иначе набор схем быстро превращается в трудно поддерживаемый и дублирующийся код. Библиотека Yup предоставляет гибкий инструмент описания правил, однако без системной организации она теряет значительную часть своей пользы.
В основе устойчивой архитектуры схем лежит разделение ответственности. Схемы не должны существовать как разрозненные объекты внутри компонентов интерфейса или сервисов бизнес-логики. Их целесообразно выделять в отдельный слой, который выполняет исключительно задачу описания правил валидации.
Ключевые принципы:
Изоляция схем от UI-слоя Логика валидации не привязывается к конкретным компонентам интерфейса. Это снижает связанность и упрощает переиспользование.
Переиспользуемость фрагментов Общие правила (например, email, пароль, телефон) выносятся в отдельные базовые схемы.
Композиционность Сложные схемы собираются из более мелких блоков.
Единая точка определения правил Каждое правило существует в одном месте, исключая расхождения между формами.
При масштабировании проекта структура папок играет критическую роль. Типичная организация может выглядеть следующим образом:
src/
validation/
schemas/
auth/
login.schema.js
register.schema.js
user/
profile.schema.js
settings.schema.js
base/
string.schema.js
number.schema.js
date.schema.js
shared/
email.schema.js
password.schema.js
phone.schema.js
index.js
Такое разделение позволяет:
shared.Базовые схемы представляют собой строительные блоки, из которых формируются более сложные структуры. Они определяют минимальные ограничения и часто используются как основа для расширения.
Пример базового типа строки:
import * as Yup from 'yup';
export const baseString = Yup.string()
.trim()
.strict(true);
Расширение базовой схемы:
import { baseString } from '../base/string.schema';
export const emailSchema = baseString
.email('Некорректный формат email')
.required('Email обязателен');
Такой подход снижает дублирование и обеспечивает единообразие поведения.
В реальных проектах часто встречаются повторяющиеся сущности: email, пароль, телефон, имя пользователя. Их выделение в отдельный слой позволяет централизовать изменения.
Пример схемы пароля:
import * as Yup from 'yup';
export const passwordSchema = Yup.string()
.min(8, 'Минимум 8 символов')
.matches(/[A-Z]/, 'Должна быть хотя бы одна заглавная буква')
.matches(/[0-9]/, 'Должна быть хотя бы одна цифра')
.required('Пароль обязателен');
Использование в разных формах:
import { passwordSchema } from '../shared/password.schema';
export const registerSchema = Yup.object({
password: passwordSchema,
confirmPassword: Yup.string()
.oneOf([Yup.ref('password')], 'Пароли не совпадают')
});
Одной из сильных сторон Yup является возможность комбинировать схемы. Это особенно важно при построении сложных объектов.
import * as Yup from 'yup';
import { emailSchema } from '../shared/email.schema';
import { passwordSchema } from '../shared/password.schema';
export const userSchema = Yup.object({
email: emailSchema,
password: passwordSchema
});
Для расширения существующих схем используется
concat:
const baseUser = Yup.object({
email: emailSchema
});
const extendedUser = baseUser.concat(
Yup.object({
role: Yup.string().required()
})
);
Такой подход позволяет постепенно наращивать сложность без дублирования структуры.
При росте системы важно группировать схемы по бизнес-областям. Это упрощает сопровождение и снижает когнитивную нагрузку.
Пример доменной структуры:
Каждый домен содержит собственные схемы, не зависящие напрямую от других областей, за исключением общих блоков.
В сложных формах часто требуется динамическая валидация. Yup
предоставляет механизм when, позволяющий изменять правила в
зависимости от значений других полей.
import * as Yup from 'yup';
export const deliverySchema = Yup.object({
hasAddress: Yup.boolean(),
address: Yup.string().when('hasAddress', {
is: true,
then: schema => schema.required('Адрес обязателен'),
otherwise: schema => schema.notRequired()
})
});
Подобные конструкции целесообразно ограничивать, так как чрезмерное усложнение логики снижает предсказуемость схем.
Некоторые правила требуют доступа сразу к нескольким полям. В таких
случаях используется test на уровне объекта.
export const paymentSchema = Yup.object({
amount: Yup.number().required(),
balance: Yup.number().required()
}).test(
'balance-check',
'Недостаточно средств',
function (values) {
return values.balance >= values.amount;
}
);
Такие проверки следует выносить в отдельные функции для повторного использования.
При увеличении количества схем полезно вводить единый экспортный модуль:
export { loginSchema } from './auth/login.schema';
export { registerSchema } from './auth/register.schema';
export { profileSchema } from './user/profile.schema';
Это позволяет импортировать схемы из одного источника:
import { loginSchema } from '@/validation';
Схемы валидации являются чистой бизнес-логикой и хорошо поддаются модульному тестированию.
Пример теста:
import { emailSchema } from './email.schema';
test('валидный email проходит проверку', async () => {
await expect(emailSchema.validate('test@mail.com')).resolves.toBeDefined();
});
test('невалидный email вызывает ошибку', async () => {
await expect(emailSchema.validate('invalid')).rejects.toThrow();
});
Тестирование схем особенно важно при использовании сложной композиции и условных правил.
При использовании TypeScript схемы Yup могут быть связаны с типами данных, что снижает расхождение между валидацией и типизацией.
import * as Yup from 'yup';
export const userSchema = Yup.object({
email: Yup.string().email().required(),
age: Yup.number().required()
});
export type User = Yup.InferType<typeof userSchema>;
Это позволяет синхронизировать модель данных и правила валидации.
В практике разработки часто встречаются типичные проблемы:
when;Устранение этих проблем требует дисциплинированного подхода к структуре проекта.
При увеличении количества форм и сущностей важно поддерживать предсказуемую архитектуру. Основные механизмы масштабирования:
object и concat;Такая организация позволяет сохранять управляемость даже при значительном росте кодовой базы и усложнении бизнес-логики.