Загрузка...

Laravel как язык бизнеса: Осваиваем DDD без боли и лишних сложностей

DDD(Domain Driven Design) - принцип проектирования и разработки систем и процессов

Введение

Domain-Driven Design — это архитектурный подход, в котором бизнес-логика становится основой кода приложения, а все ключевые части системы описываются так, как это реально устроено у бизнеса. В Laravel DDD особенно полезен для больших проектов с меняющимися требованиями и сложной бизнес-логикой. Разберём ключевые принципы и представим, как на практике устроить DDD-проект на Laravel.

Принципы DDD

  • Единый язык (ubiquitous language) — бизнес и разработчики используют одни и те же термины для описания системы. В коде эти термины отражаются в названиях сущностей, сервисов и событий.
  • Ограниченные контексты (bounded context) — сложную систему разбивают на отдельные области ответственности, где каждое понятие имеет свой смысл.
  • Валидация бизнес-правил — централизуйте бизнес-логику, чтобы из разных частей системы не возникало противоречий.
  • Моделирование предметной области — определите, что важно для вашей компании, и делайте эти сущности первоклассными жителями кода (например, Order, Invoice, Product).

Архитектура DDD в Laravel-проектах

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

  • Domain — бизнес-объекты, сущности, value-объекты, агрегаты.
  • Application — сервисы, реализующие пользовательские сценарии, операции.
  • Infrastructure — работа с внешними системами (БД, очереди, API), реализация репозиториев.
  • Http — контроллеры, которые обрабатывают запросы, вызывают Application сервисы и возвращают ответы.

Структура и примеры кода

Структура может выглядеть так:

app/
├── Domain/
│   ├── Order/
│   │   ├── Entities/
│   │   ├── ValueObjects/
│   │   ├── Services/
│   ├── User/
├── Application/
│   ├── Services/
│   ├── DTO/
├── Infrastructure/
│   ├── Repositories/
│   ├── Services/
│   ├── LaravelAdapters/
├── Http/
│   ├── Controllers/

Объяснения:

  • Внутри Domain описаны объекты предметной области — сущности, value объекты, сервисы бизнес-логики.
  • Application реализует координаторов (use-cases), которые разруливают сценарии для конечных пользователей.
  • Infrastructure отвечает за интеграцию — БД, очереди, логирование, работу с Eloquent и внешними API.
  • Http — обычные контроллеры Laravel, максимально тонкие, только делегируют работу в Application слой.

Bounded Contexts и примеры

Например, у интернет-магазина есть контексты «Заказы», «Пользователь», «Каталог». В каждом из них свой набор бизнес-объектов, правил и даже значение терминов («Товар» для склада и «Товар» для клиента могут отличаться).

Пример value object в Laravel:

// app/Domain/Order/ValueObjects/OrderStatus.php
namespace App\Domain\Order\ValueObjects;

class OrderStatus {
    private string $status;
    public function __construct(string $status) {
        if (!in_array($status, ['new', 'paid', 'shipped', 'cancelled'])) {
            throw new \InvalidArgumentException('Invalid order status');
        }
        $this->status = $status;
    }
    public function get(): string { return $this->status; }
}

Пример Entity:

// app/Domain/Order/Entities/Order.php
namespace App\Domain\Order\Entities;

use App\Domain\Order\ValueObjects\OrderStatus;

class Order {
    private OrderStatus $status;
    public function __construct(OrderStatus $status) {
        $this->status = $status;
    }
    public function pay() {
        // бизнес-логика перехода статуса
        $this->status = new OrderStatus('paid');
    }
}

Реализация паттерна в Laravel

  • Сервис-провайдеры Laravel пригодятся для подключения Application и Domain сервисов через DI.
  • Контроллеры — делают минимум: принимают запрос, вызывают Application сервис, возвращают ответ.
  • Eloquent может использоваться как инфраструктурный слой: маппит Domain объекты на таблицы БД, не содержит бизнес-логики.

Пример Application Service:

namespace App\Application\Services;
use App\Domain\Order\Entities\Order;
use App\Domain\Order\ValueObjects\OrderStatus;
use App\Infrastructure\Repositories\OrderRepository;

class OrderService {
    private $orderRepository;
    public function __construct(OrderRepository $orderRepository) {
        $this->orderRepository = $orderRepository;
    }
    public function payOrder($orderId) {
        $order = $this->orderRepository->find($orderId);
        $order->pay();
        $this->orderRepository->save($order);
    }
}

Контроллер:

namespace App\Http\Controllers;
use App\Application\Services\OrderService;

class OrderController extends Controller {
    private $orderService;
    public function __construct(OrderService $orderService) {
        $this->orderService = $orderService;
    }
    public function pay($orderId) {
        $this->orderService->payOrder($orderId);
        return response()->json(['status' => 'ok']);
    }
}

Тестирование домена

Domain-слой легко тестировать отдельно от инфраструктуры — пишите unit-тесты на бизнес-логику и value-объекты без обращения к БД. Для интеграционных тестов удобно использовать фабрики и моки.

Проблемы и советы

  • Типичные ошибки: перенос бизнес-логики в Eloquent модели, отсутствие разделения контекстов, избыточная усложненность структуры.
  • Рекомендации: внедряйте DDD постепенно, сначала вынесите бизнес-логику из контроллеров и инфраструктуры, обособьте контексты, заставьте бизнес и разработку “говорить на одном языке”.

Заключение

DDD — мощный инструмент для построения больших Laravel-проектов с устойчивой архитектурой и прозрачными бизнес-процессами. Главное — моделируйте домен, выделяйте границы и сохраняйте бизнес-логику в центре приложения, окружая её технической инфраструктурой.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *