главная/Доменные примитивы
Доменные примитивы

Доменные примитивы

Доменные примитивы — это небольшие Value Object-ы, которые инкапсулируют важное бизнес-значение и правила валидации.

Зачем нужны?

  • Гарантия корректности данных — нельзя создать Email без проверки.
  • Правила в одном месте — валидация email не расползается по проекту.
  • Устойчивость к ошибкам — невозможно случайно передать “строку, которая должна быть email”, но ей не является.

Когда важные значения передаются как обычные строки — например email или UUID — мы создаём себе лишнюю работу.

Пример классического подхода:

$email = $_POST['email'] ?? '';
$service->registerUser($email);

Каждый раз, в каждом месте, где проходит значение, нужно:

  • проверить, что поле не пустое
  • провалидировать формат email
  • нормализовать (обрезать пробелы, привести к lowercase)
  • убедиться, что оно прошло все фильтры
  • повторить проверки в сервисе
  • повторить проверки в репозитории перед записью

Любая пропущенная проверка = баг.

Решение — доменные примитивы

Пример 1

Это email пользователя, который мы получаем из формы и передаём дальше.

Валидируем и нормализуем его теперь только один раз — в конструкторе.

final class Email
{
    private string $value;

    public function __construct(string $value)
    {
        if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException("Invalid email: $value");
        }

        $this->value = strtolower(trim($value));

        // Здесь можно добавить свои проверки:
        // - запрещённые домены
        // - длина
        // - бизнес-правила
    }

    // остальные методы...
}

Пример использования

$emailInput = $_POST['email'] ?? null;

if (!$emailInput) {
    throw new RuntimeException("Email is required.");
}

try {
    $email = new Email($emailInput); // валидация и нормализация происходят здесь
} catch (InvalidArgumentException $e) {
    // пример обработки ошибок
    // можно вывести сообщение пользователю или записать в лог
    die("Ошибка: " . $e->getMessage());
}

$service->registerUser($email);
  • дальше по коду email уже гарантированно валиден
  • не нужно повторять проверки
  • весь код упрощается

Пример 2

Это уникальный идентификатор события (Event), который используется в системе повсеместно.

Раньше приходилось валидировать UUID в контроллере, сервисе, репозитории — теперь только один раз.

final class EventUuid
{
    private string $value;

    public function __construct(string $value)
    {
        if (!preg_match('/^[0-9a-fA-F-]{36}$/', $value)) {
            throw new InvalidArgumentException("Invalid UUID: $value");
        }

        $this->value = strtolower($value);

        // Здесь можно добавить свои проверки:
        // - версия UUID
        // - запрещённые значения
        // - проверка на корректность формата
    }

    // остальные методы...
}

Пример использования

$uuidInput = $_GET['event_uuid'] ?? null;

if (!$uuidInput) {
    throw new RuntimeException("Event UUID is required.");
}

try {
    $uuid = new EventUuid($uuidInput); // строгая проверка прямо здесь
} catch (InvalidArgumentException $e) {
    die("Ошибка UUID: " . $e->getMessage());
}

$eventService->load($uuid);
  • дальше по системе UUID всегда валидный
  • нет повторной валидации
  • нет риска случайно передать мусор