Верх страницы
Обложка к записи DDD: сравнение Laravel-data и Symfony Validator Object как DTO
Время для прочтения: 1 мин. 17 сек.

DDD: сравнение Laravel-data и Symfony Validator Object как DTO

Один из самых простых шагов при переходе приложения к кодовой базе Domain Driven Design — создание объектов передачи данных, или DTO для краткости.

Базовый DTO — это класс с свойствами. Но это неудобный способ работы с составными DTO.

// AddressDTO.php
readonly class AddressDTO {
   public function __construct(
                         public ?string $street = null,
                         public ?string $streetNumber = null,
                         public ?string $city = null,
                         public ?string $postalCode = null,
                         public ?string $country = null,
   ){}
}
// PersonDTO.php
readonly class PersonDTO {
  public function __construct(
                         public ?string $name = null,
                         public ?AddressDTO $address = null,
  ){}
}
// какой-то контроллер
$person = new PersonDTO(
                name: $request->get('name'),
                address: new AddressDTO(
                              street: $request->get('street'),
                              // ...
                          ),
         );    

А теперь глянем, чем эти два пакета отличаются. Компонент Symfony Validator не имеет встроенной функциональности трансформации. Пакет Laravel-data предоставляет для этого методы from.

// AddressDTO.php
use Spatie\LaravelData\DTO;

class AddressDTO extends DTO {
   public function __construct(
                         public ?string $street = null,
                         public ?string $streetNumber = null,
                         public ?string $city = null,
                         public ?string $postalCode = null,
                         public ?string $country = null,
   ){}
}
// PesonDTO.php
use Spatie\LaravelData\DTO;

class PersonDTO extends DTO {
  public function __construct(
                         public ?string $name = null,
                         public ?AddressDTO $address = null,
  ){}
}
// какой-то контроллер
$pserson = PersonDTO::from([
   'name' => $request->get('name'),
   'address' => [
      'street' => $request->get('street')
   ],
]);

Внимательные читатели заметили, что DTO в этом примере потеряли свойство readonly. Это потому, что класс DTO не является readonly. Хотя изменяемые DTO не проблема, возможность иметь неизменяемые DTO делает код более надежным.

Зачем сравнивать?

Почему я сравниваю пакет, ориентированный на объекты, с пакетом, ориентированным на валидацию?

Одно из действий с DTO — валидация. Поскольку валидация — это бизнес-логика, этот код должен происходить в домене. Не хочу изобретать велосипед, поэтому выбираю компонент Symfony validator.

Laravel-data поставляется с методами валидации из коробки.

Согласно определению, DTO не предназначен для валидации. Валидация может происходить до преобразования данных в DTO или после трансформации.

DTO — хорошее место для хранения правил валидации. Оба пакета предоставляют это через атрибуты свойств.

class AddressDTO extends DTO {
    public function __construct(
        #[Required]
        public ?string $street = null,
        #[Required]
        public ?string $streetNumber = null,
        #[Required]
        public ?string $city = null,
        #[Required]
        public ?string $postalCode = null,
        #[Required]
        public ?string $country = null,
    ){}
}

Главное отличие: валидация DTO в Laravel-data использует валидатор Laravel, а компонент Symfony validator не зависит от Symfony.

Еще одно преимущество компонента Symfony validator — правила можно группировать. Это позволяет использовать один DTO с разными бизнес-требованиями.

// AddressDTO.php
use Symfony\Component\Validator\Constraints as Assert;

readonly class AddressDTO {
   public function __construct(
                         #[Assert\NotBlank(groups: ['CreateAddress'])]
                         public ?string $street = null,
                         #[Assert\NotBlank(groups: ['CreateAddress'])]
                         public ?string $streetNumber = null,
                         #[Assert\NotBlank(groups: ['CreateAddress'])]
                         public ?string $city = null,
                         #[Assert\NotBlank(groups: ['CreateAddress', 'ChangePostalCode'])]
                         public ?string $postalCode = null,
                         #[Assert\NotBlank(groups: ['CreateAddress'])]
                         public ?string $country = null,
   ){}
}
// в коде домена
$validator->validate($addressDTO, groups: ['ChangePostalCode']);

Заключение

Стало очевидно, что использование класса DTO из Laravel-data не подходит для перехода к DDD-кодовой базе. Сильные стороны пакета в другом.

Создание DTO явно может быть правильным путем, даже если это немного больше работы (если не использовать ИИ).

Недостаток обоих пакетов: не так просто сопоставить сообщения об ошибках с полями ввода, поскольку валидация происходит в DTO.

Комментарии
Подписаться
Уведомить о
guest

0 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии
Предыдущая запись

Давайте дружить
в Telegram

Авторский блог вашего покорного слуги в Telegram про web, программирование, алгоритмы, инструменты разработчика, WordPress, Joomla, Opencart, Symfony, Laravel, Moonshine, фильмы и сериалы