Использовать плагины контроллера ZF2 для служб приложений в DDD?

В настоящее время я использую DDD (Domain Driven Design) для нового проекта Zend Framework 2. Все работает нормально, но у меня есть вопрос относительно сервисов приложений.

Я понял, что сервисы приложений расположены на уровне приложений и являются своего рода точкой входа в доменную логику. Они могут получить доступ к доменным службам или хранилищу, например.

Теперь мне интересно, имеет ли смысл реализовывать сервисы приложений как плагины контроллера. В классическом приложении MVC этот плагин контроллера может обрабатывать результаты от названных доменных служб или репозиториев. В зависимости от этих результатов они могут генерировать ответ перенаправления или передавать данные / форму в ViewModel. Если эта логика заключена в плагин, мой контроллер должен только вызвать плагин и вернуть результат плагина.

Я совершенно не прав? Или вы предпочли бы сохранить логику, как реагировать на результаты доменной службы или хранилища в контроллере?

С уважением,

Ralf

2 ответа

Конечно, это отчасти субъективно, и у людей сильные мнения о таких вещах... так вот мое:

  1. Плагины контроллера содержат код, который универсален для любого действия MVC/REST, а бизнес-логика не универсальна. Плагины должны облегчать "контроль" запросов / ответов, а не бизнес-логику, которая находится на уровне модели. Связывание этого сделает его менее пригодным для повторного использования, например, для консольных действий. Кроме того, было бы менее вероятно использовать бизнес-логику с какой-либо другой структурой.
  2. Это неудобно для проверки. Внедрение плагинов контроллера в качестве параметров конструктора класса контроллера было бы излишним, поскольку они уже доступны в диспетчере плагинов, введенном в AbstractActionController или же AbstractRestfulController, Отсутствие зависимостей, введенных очевидным / видимым способом (например, методом через корыто), затрудняет выяснение того, от чего фактически зависит класс контроллера. Также, так как все плагины (AbstractPlugin related) зависит от экземпляра контроллера, переключение контекста с http на консоль (например, для теста phpunit) может стать проблематичным. Также тестовая логика, написанная / сделанная доступной как плагин контроллера, рано или поздно переросла бы в включение объектов запросов / ответов в тесты, и это лишняя сложность.
  3. Это не интуитивно понятно. Когда я слышу плагин, я думаю о чем-то маленьком. Не полный код бизнес-логики похоронен под таким неприметным именем. Поэтому, когда у меня мало времени для отладки чьего-то кода, последнее, что мне нужно, чтобы такие вещи были неуместны.

Снова я хотел бы повторить, это только мое мнение. Я узнал, что многие шаблоны могут разрушиться при достаточно странном сценарии использования, но вышеприведенные пункты имели смысл для меня и моей команды до сих пор.

В качестве примера моего решения вы можете увидеть действие контроллера:

    public function showAction()
    {
        $service = $this->readProductEntityCommand;
        $service->setId($this->params()->fromRoute('id'));

        try {
            $result = $service->execute();
        } catch (ProductException $e) {
            $this->flashMessenger()->addMessage($e->getMessage());

            return $this->redirect()->toRoute('part3/product');
        }

        return new ViewModel(
            array(
                'productEntity' => $result->getData(),
            )
        );
    }

А вот пример сборки службы приложения как объекта команды

class ReadProductEntityCommand implements CommandInterface
{
    protected $productRepository;

    protected $id;

    public function __construct(ProductRepositoryInterface $productRepository)
    {
        $this->productRepository = $productRepository;
    }

    public function setId($id)
    {
        $this->id = $id;
    }

    public function execute()
    {
        if (is_null($this->id)) {
            throw new ProductIdInvalidException(
                'Produkt ID wurde nicht angegeben.'
            );
        }

        try {
            $product = $this->productRepository->getProduct(
                new ProductIdCriterion(
                    new ProductId($this->id)
                )
            );
        } catch (\Exception $e) {
            throw new ProductNotFoundException(
                'Es konnten kein Produkt gelesen werden.'
            );
        }

        if ($product === false) {
            throw new ProductNotFoundException('Produkt wurde nicht gefunden.');
        }

        $result = new Result();
        $result->setValid(true);
        $result->setData($product);
        $result->setMessage('Produkt wurde gelesen.');

        return $result;
    }
}

Может быть, это поможет кому-то в будущем.

Другие вопросы по тегам