Стоит ли делать валидацию прямо в Entity? Ответ изнутри DDD

Когда мы говорим о Domain-Driven Design, один из частых вопросов, которые я слышу:

“А нужно ли вообще делать валидацию в Entity? Это же бизнес-логика, а не проверка форм.”

Ответ на самом деле — да и нет. Зависит от сути валидации.

Разберёмся по порядку.


✅ Что действительно должно быть в Entity

Если коротко: всё, что относится к бизнес-инвариантам, должно валидироваться на уровне Entity.

Инварианты — это такие правила, которые всегда должны быть соблюдены, независимо от того, как ты создаёшь объект:
через API, из CLI, через Event, или даже в тесте.

Примеры инвариантов:

ПравилоПочему в Entity?
Имя пользователя не может быть пустымЭто требование бизнес-домена
Возраст пользователя ≥ 14 летЭто логика продукта
Email не должен повторяться (если это важно на уровне домена)Это правило модели

Если ты не проконтролируешь это в Entity — твой домен становится хрупким. Создать невалидного пользователя сможет любой, у кого есть доступ к new User(...).


🚫 А что лучше оставить за пределами Entity?

Не стоит превращать Entity в форму. Есть проверки, которые не имеют отношения к бизнесу — они касаются специфики взаимодействия с внешним миром:

ПроверкаГде её делать?
Номер телефона в формате +7...В DTO или Symfony Validator
Файл должен быть JPEG ≤ 5 МБВ инфраструктуре (например, в хендлере загрузки)
Поле description обязательноВ форме или DTO
Капча или токен подтвержденияНа уровне Application

Entity — не полицейский, проверяющий каждое поле. Он — гарант доменной целостности.


🔍 Почему нельзя полагаться только на DTO

DTO — это всего лишь транспорт, оболочка вокруг входящих данных.
Сегодня ты получил их из API, завтра из теста, послезавтра из Kafka.

Если проверка “имя не может быть пустым” живёт только в DTO, то:

$user = new User('');

спокойно создаст “битого” пользователя где-нибудь в другом месте кода. И ты даже не узнаешь об этом — пока не станет слишком поздно.


🧪 Хорошая практика

Простой и рабочий способ:

final class User
{
    public function __construct(private string $name)
    {
        if (mb_strlen($name) < 2) {
            throw new \DomainException('Имя слишком короткое');
        }
    }
}

Так ты гарантируешь, что User всегда создаётся в валидном состоянии.

И тебе уже не страшны ошибки из-за пропущенной валидации в 148 месте кода.


⚖️ Компромисс: фабрики и named constructors

Если ты не хочешь “мусорить” Entity исключениями, можешь обернуть валидацию в фабрику:

final class UserFactory
{
    public static function fromDto(UserCreateDTO $dto): User
    {
        if (mb_strlen($dto->name) < 2) {
            throw new \DomainException('Имя слишком короткое');
        }

        return new User($dto->name);
    }
}

Но всё равно — валидация, относящаяся к бизнесу, должна быть рядом с моделью.


🧩 Как это сформулировать одной фразой?

Entity отвечает за соблюдение бизнес-инвариантов. DTO — за валидацию пользовательского ввода.


📌 Вывод

Если ты работаешь по DDD, помни:
твоё ядро — это место силы. Оно должно быть самодостаточным.

Иначе ты просто сделал ещё одну анемичную модель с кучей getter’ов и null-сущностей.


Если статья была полезна — буду рад обратной связи. А если ты видишь ситуацию по-другому — пиши, обсудим как взрослые люди в комментариях.

Рейтинг
( 3 оценки, среднее 5 из 5 )
Максим Гречушников/ автор статьи
Мне 40 лет, и больше половины жизни я занимаюсь разработкой. За это время я работал с корпоративными системами, highload-проектами, интеграциями, платежными решениями, внутренними платформами и legacy-кодом, который пережил не одного разработчика. Некоторые системы, кажется, переживут и нас. Моя специализация — PHP, Laravel, backend-архитектура, DDD, тестирование, рефакторинг и техническое лидерство. Мне интересны задачи, где нужно соединить код, бизнес, данные, процессы и здравый смысл. Последние годы я много работаю с LLM и AI-инструментами: использую их для проектирования, анализа кода, документации, тестов, ревью, работы с legacy и построения инженерных процессов. Для меня LLM — это не замена разработчику, а усилитель для тех, кто умеет думать системно. В блоге я пишу о Laravel, PHP, архитектуре, тестировании, инженерной практике, управлении разработкой и реальном применении AI в работе программиста.
Понравилась статья? Поделиться с друзьями: