В Laravel разработке рано или поздно возникает вопрос:
что использовать — Eloquent модель или DTO?
На старте проекта всё просто — модели хватает.
Но по мере роста приложения появляются:
- сервисный слой
- очереди
- интеграции
- сложная бизнес-логика
- тестирование
И тут становится очевидно: модель — не всегда правильный инструмент для передачи данных.
В этой статье разберём:
- что такое DTO в Laravel
- чем DTO отличается от модели
- когда использовать DTO
- как правильно внедрить DTO в архитектуру проекта
Что такое модель в Laravel (Eloquent Model)
Eloquent-модель — это реализация паттерна Active Record, встроенная в Laravel.
Модель отвечает за:
- работу с базой данных
- связи (
hasOne,belongsTo,hasMany) - события (
creating,updated) - касты атрибутов
- query builder
- сохранение и обновление записей
Пример модели:
class User extends Model
{
protected $fillable = ['name', 'email'];
}
Использование:
$user = User::create([
'name' => 'Alex',
'email' => 'alex@example.com',
]);
Важно
Eloquent-модель:
- связана с БД
- может выполнять запросы
- содержит поведение
- является инфраструктурным слоем
Это не просто объект с данными, а активный объект, который управляет своим состоянием.
Что такое DTO (Data Transfer Object) в Laravel
DTO (Data Transfer Object) — это объект для передачи данных между слоями приложения.
Он:
- не работает с БД
- не содержит бизнес-логики
- не выполняет запросы
- хранит только данные
Пример DTO:
readonly class CreateUserDTO
{
public function __construct(
public string $name,
public string $email,
public string $password,
) {}
}
DTO используется для:
- передачи данных из контроллера в сервис
- передачи данных в Job
- работы с API
- разделения слоёв приложения
DTO и модель в Laravel: ключевая разница
| Модель (Eloquent) | DTO |
|---|---|
| Связана с БД | Не связана с БД |
| Active Record | Data Carrier |
| Может выполнять запросы | Не выполняет запросы |
| Содержит поведение | Только структура данных |
| Инфраструктурный слой | Транспортный слой |
Главное отличие:
Модель — это инструмент работы с данными.
DTO — это контейнер для передачи данных.
Когда использовать модель в Laravel
Модель уместна, если:
- нужно сохранить данные в БД
- требуется работа со связями
- используются касты
- выполняются ORM-запросы
- это простой CRUD-проект
Пример:
$user = User::find($id);
$user->update([...]);
Здесь DTO не нужен.
Когда использовать DTO в Laravel
DTO становится полезным в следующих случаях.
1. Передача данных в сервисный слой
Плохая практика:
$userService->create($request->all());
Проблемы:
- передаётся массив
- нет строгой структуры
- нет типизации
- зависимость от HTTP
Правильный подход:
$dto = new CreateUserDTO(
name: $request->name,
email: $request->email,
password: $request->password,
);
$userService->create($dto);
Теперь:
- сервис не зависит от Request
- есть контракт
- код легче тестировать
2. Разделение слоёв архитектуры
Если проект использует:
- Service Layer
- Actions
- Domain-подход
- Clean Architecture
- DDD
DTO становится обязательным элементом.
Правильная цепочка:
Controller → FormRequest → DTO → Service → Model
DTO разрывает зависимость между HTTP и инфраструктурой.
3. Использование очередей (Jobs)
Передавать модель напрямую в Job — плохая практика:
SendWelcomeEmail::dispatch($user);
Это может привести к:
- неожиданной сериализации
- скрытым запросам
- ошибкам при изменении модели
Лучше:
SendWelcomeEmail::dispatch(
new UserDTO(
id: $user->id,
email: $user->email,
)
);
4. Интеграции и API
DTO удобно использовать для:
- внешних API
- webhook-обработчиков
- трансформации данных
- ответов REST API
Он делает структуру данных явной и стабильной.
DTO vs Model: архитектурный аспект
Ошибка многих Laravel-разработчиков — использовать модель как универсальный объект.
Пример плохой архитектуры:
public function create(User $user)
{
// бизнес-логика
}
Метод уже зависит от базы данных.
Правильнее:
public function create(CreateUserDTO $dto)
{
// бизнес-логика
}
Теперь:
- сервис не знает о БД
- код легче тестировать
- архитектура становится чище
DTO и FormRequest — это не одно и то же
FormRequest:
- валидирует входные данные
- работает на уровне HTTP
DTO:
- хранит валидированные данные
- используется в бизнес-логике
Идеальная схема:
Request → FormRequest (валидация) → DTO → Service → Model
Нужно ли использовать DTO в каждом Laravel-проекте
Нет.
Если проект:
- небольшой
- простой CRUD
- без сложной логики
- без интеграций
- без сервисного слоя
DTO может быть избыточным.
Но если проект:
- растёт
- становится сложным
- покрывается тестами
- использует очереди
- работает с API
DTO значительно упрощает поддержку.
Лучшие практики использования DTO в Laravel
Делайте DTO неизменяемыми
Используйте readonly (PHP 8.2+):
readonly class UpdateProfileDTO
{
public function __construct(
public string $name,
public string $bio,
) {}
}
Используйте фабричный метод fromRequest
class CreateUserDTO
{
public function __construct(
public string $name,
public string $email,
) {}
public static function fromRequest(Request $request): self
{
return new self(
name: $request->string('name'),
email: $request->string('email'),
);
}
}
Контроллер становится чище:
$dto = CreateUserDTO::fromRequest($request);
$userService->create($dto);
Использование пакетов для DTO
Для крупных проектов можно использовать:
- spatie/laravel-data
- кастомные трансформеры
- Value Objects
Но в большинстве случаев достаточно обычных PHP-классов.
Частые ошибки при работе с DTO
❌ Использовать DTO вместо модели для сохранения
❌ Передавать массивы вместо объектов
❌ Передавать Eloquent-модель в сервис
❌ Смешивать DTO и бизнес-логику
DTO должен оставаться простым и предсказуемым.
Итог: что выбрать — DTO или модель
DTO и модель в Laravel не конкурируют.
- Модель — работа с базой данных.
- DTO — передача данных между слоями.
Если вы строите масштабируемый Laravel-проект,
DTO делает код:
- более чистым
- предсказуемым
- тестируемым
- архитектурно устойчивым
Именно поэтому в серьёзных проектах DTO почти всегда присутствует.