✔️ 1. Структура проекта по смыслу
Начнём с того, что создадим папку
app/Domains. Это не техническое разделение (типа Models, Controllers и так далее), а смысловое — по бизнес-сущностям. Например:
app/
Domains/
User/
Actions/
DTO/
Models/
Services/
Order/
...
Каждая папка отвечает за отдельную часть бизнес-логики. Это уже облегчает навигацию в проекте и помогает новичкам быстрее понять, где что лежит.
✔️ 2. Actions — конкретные действия
Actions — это реализация бизнес-сценариев. Один класс — одно действие. Например, CreateUserAction
занимается только созданием пользователя. Всё остальное — не его дело.
class CreateUserAction
{
public function execute(UserData $data): User
{
return User::create([
'name' => $data->name,
'email' => $data->email,
'password' => Hash::make($data->password),
]);
}
}
Это удобно, потому что ты всегда знаешь, где искать логику создания пользователя. И если бизнес меняется — правишь только здесь.
✔️ 3. DTO — никакой грязи из запроса
Вместо того чтобы передавать Request или массивы, создаём отдельный объект данных.
class UserData
{
public function __construct(
public string $name,
public string $email,
public string $password,
) {}
public static function fromRequest(Request $request): self
{
return new self(
name: $request->input('name'),
email: $request->input('email'),
password: $request->input('password'),
);
}
}
DTO — это простой способ структурировать входные данные и сделать их типизированными. Удобнее отлаживать, тестировать и передавать дальше.
✔️ 4. Контроллеры без логики
Контроллеры остаются тонкими. Они не должны думать, как создавать пользователя — они просто принимают данные, вызывают нужное действие и отдают ответ.
public function store(StoreUserRequest $request)
{
$data = UserData::fromRequest($request);
$user = app(CreateUserAction::class)->execute($data);
return redirect()->route('users.index');
}
Контроллер — это просто связка между HTTP и логикой. Чем меньше кода внутри, тем легче читать и тестировать.
✔️ 5. Services, Events и прочее
Если логика сложная — выноси в сервисы. Например, UserService может заниматься отправкой письма или логированием.
Events тоже отлично ложатся в DDD — можно генерировать события вроде UserRegistered прямо внутри Action и обрабатывать их отдельно.
event(new UserRegistered($user));
Так ты разделяешь то, что "происходит", и то, "что делать с этим дальше".
✔️ 6. Не перепутай архитектуру с бюрократией
DDD — это не про количество слоёв. Если начинаешь создавать 10 интерфейсов до того, как написал хоть один класс — ты уже не туда свернул.
Начинай с одной сущности. Выдели DTO, сделай Action. Потом усложняй. Главное — смысл, а не формальность.
✔️ 7. Когда оно окупается
Сначала кажется, что DDD — лишняя работа. Но когда проект растёт, появляются новые правила, новые роли и новые требования — тебе не нужно лезть в старые контроллеры или модели, чтобы что-то добавить.
Ты просто создаёшь новый Action, новый DTO, и всё.
✔️ Вывод
Domain Driven Design в Laravel — это не тяжело. Это просто привычка разделять код по смыслу, а не по шаблону.
Делай по чуть-чуть: одна сущность — одна логика — один смысл. Без фанатизма, но с уважением к структуре. Спустя месяц ты уже не захочешь писать по-старому.