Введение

Разработка программного обеспечения — это не только технический вопрос, но и понимание бизнеса, для которого мы создаем решения. Разработка на основе предметной области (DDD) является ответом на эти проблемы, предлагая подход, ориентированный на моделирование основной бизнес-логики. В этой статье мы более подробно рассмотрим DDD, его преимущества и практические способы его реализации в Symfony 6.

Что такое доменно-ориентированная разработка (DDD)?

DDD — это подход к разработке программного обеспечения, который фокусируется на моделировании бизнес-реальности. Ключевым элементом DDD является «повсеместный язык», который позволяет лучше понимать и общаться между бизнесом и командами разработчиков. Идея подхода DDD (аббревиатура от Domain — Driven — Design) очень проста: объединить все команды, участвующие в разработке продукта:

  • Обеспечьте плавную и двустороннюю связь между бизнес-командой и командой разработчиков.
  • Убедитесь, что разработанный продукт соответствует ожиданиям бизнеса.
  • Обеспечить итерируемость и ремонтопригодность продукта.

Основная идея реализации DDD заключается в том, чтобы максимально отделить область нашего приложения от инфраструктуры, которую мы используем для реализации проекта («разделение задач»). То есть, если мы используем Symfony для разработки проекта, желательно, чтобы сущности нашей предметной области совершенно не знали о существовании Доктрины ORM или каких-либо компонентов, которые Symfony предлагает нам для реализации функциональности.

Компоненты DDD:

  1. Модель/домен: представление нашего домена в коде. Модель содержит все ключевые понятия и бизнес-правила нашего приложения. Домен — это «сфера знаний», например бизнес, которым управляет компания. Домен также называют «проблемным пространством», то есть проблемой, для которой мы должны разработать решение.
  2. Агрегаты: набор объектов, которые рассматриваются как единое целое в целях изменения данных. У каждого агрегата есть главный объект, называемый корнем агрегата, через который происходит все взаимодействие с агрегатом.
  3. Ограниченный контекст: граница, в пределах которой конкретная модель имеет смысл. В одном ограниченном контексте определенные слова могут иметь иное значение, чем в другом контексте.

Преимущества и недостатки DDD

Преимущества:

  1. Улучшение взаимодействия между командами. Благодаря общему языку команды бизнеса и разработчиков могут лучше общаться и понимать потребности друг друга. Этот общий язык исключает недопонимание и позволяет более эффективно сотрудничать.
  2. Лучшее понимание предметной области: DDD позволяет разработчикам глубже понять бизнес-проблемы, которые они пытаются решить. Это более глубокое понимание приводит к созданию более точных и эффективных решений.
  3. Гибкость: фокусируясь на предметной области, код становится более гибким и его легче модифицировать по мере развития бизнес-требований. Такая гибкость позволяет быстрее адаптироваться к меняющимся потребностям рынка.

Недостатки:

  1. Более высокие первоначальные усилия: реализация DDD изначально требует больше времени и усилий со стороны всей команды, чем традиционные подходы. Это требует затрат времени на начальных этапах проекта, но окупается в долгосрочной перспективе.
  2. Сложность: DDD вносит дополнительную сложность в виде новых шаблонов и концепций, которые команда должна понять и принять. Однако эта сложность необходима для достижения более глубокого понимания предметной области.

CQRS

CQRS (разделение ответственности за запросы команд) — это архитектурный шаблон, который отделяет операции чтения от операций записи. Это позволяет нам масштабировать и оптимизировать обе части системы независимо. CQRS позволяет независимо масштабировать и оптимизировать операции чтения и записи, что особенно полезно в больших и сложных системах. Этот шаблон также способствует ясности и однозначности кода, поскольку операции чтения и записи четко разделены. Это позволяет командам сосредоточиться на независимой оптимизации каждой из этих операций, что приводит к более эффективным и масштабируемым решениям.

Пример кода для CQRS:

// Write command
class CreateUserCommand {
    private $userId;
    private $username;
    // ...
}

// Write command handler
class CreateUserHandler {
    public function __invoke(CreateUserCommand $command) {
        // logic for creating a user in the database
    }
}
// Read query
class GetUserQuery {
    private $userId;
}
// Read query handler
class GetUserHandler {
    public function __invoke(GetUserQuery $query) {
        // logic for reading a user from Elasticsearch
    }
}

Шестиугольная архитектура

Шестиугольная архитектура, также известная как порты и адаптеры, представляет собой архитектурный шаблон, который способствует отделению бизнес-логики приложения от его внешних слоев, таких как база данных или пользовательский интерфейс. Это делает бизнес-логику более модульной и ее легче тестировать. Шестиугольная архитектура позволяет изолировать бизнес-логику от внешних зависимостей, что упрощает тестирование и разработку приложений. Этот шаблон также обеспечивает модульность и гибкость, упрощая обслуживание и расширение приложений.

Пример кода для шестиугольной архитектуры:

// Port
interface UserRepository {
    public function save(User $user);
    public function findById($userId);
}

// Database adapter
class DatabaseUserRepository implements UserRepository {
    public function save(User $user) {
        // logic for saving a user in the database
    }
    public function findById($userId) {
        // logic for reading a user from the database
    }
}
// Elasticsearch adapter
class ElasticsearchUserRepository implements UserRepository {
    private $client;
    public function __construct(Client $client) {
        $this->client = $client;
    }
    public function save(User $user) {
        $params = [
            'index' => 'users',
            'id'    => $user->getId(),
            'body'  => [
                'username' => $user->getUsername(),
                // ... other user fields
            ]
        ];
        $this->client->index($params);
    }
    public function findById($userId) {
        $params = [
            'index' => 'users',
            'id'    => $userId
        ];
        $response = $this->client->get($params);
        return new User($response['_source']['username']);
    }
}

Настройка Symfony Messenger

Чтобы настроить symfony/messenger в Symfony, нам нужно добавить соответствующие настройки в файл config/packages/messenger.yaml:

framework:
    messenger:
        transports:
            async: '%env(MESSENGER_TRANSPORT_DSN)%'
        routing:
            'App\Command\CreateUserCommand': async
            'App\Query\GetUserQuery': async

В приведенной выше конфигурации мы определили транспорт async, который можно использовать для асинхронной обработки команд и запросов. Затем мы определяем маршрутизацию для нашей команды CreateUserCommand и запроса GetUserQuery, указывая, что они должны обрабатываться асинхронно.

Настройка Elasticsearch в Symfony

Чтобы настроить Elasticsearch в Symfony, нам сначала нужно установить соответствующую клиентскую библиотеку Elasticsearch с помощью композитора:

composer require elasticsearch/elasticsearch

Затем мы можем настроить клиент Elasticsearch в файле config/services.yaml:

services:
    Elasticsearch\Client:
        arguments:
            $config:
                hosts: ['localhost:9200']

При указанной выше конфигурации при внедрении зависимости Elasticsearch\Client в наш сервис (например, ElasticsearchUserRepository) Symfony автоматически предоставит нам настроенный экземпляр клиента Elasticsearch.

Является ли DDD «серебряной пулей»?

Нет, DDD не всегда является оптимальным решением — такой подход требует гораздо больше времени и большего участия всей команды, чем обычная разработка. Кроме того, перенос бизнес-домена в программное обеспечение требует соблюдения определенных стандартов, которые позволяют переносить эти идеи на объекты и службы приложения. Придерживайтесь этой идеи:

DDD призывает нас максимально приблизить бизнес-логику к сущностям нашей предметной области.

Поэтому, прежде чем приступить к реализации проекта с доменным подходом, убедитесь, что у вас есть соответствующее время и участие.

Заключение

Разработка на основе предметной области — это подход, который позволяет лучше понимать и моделировать бизнес-реальность. В сочетании с современными архитектурными шаблонами, такими как CQRS и гексагональная архитектура, а также инструментами, доступными в Symfony, DDD становится еще более привлекательным подходом к разработке приложений.