Доменно-Ориентированный Дизайн На Php

Статья, можно сказать, о наболевшем вопросе.

Из-за низкого барьера входа, привычки коннектиться с MySQL, отсутствия необходимой сборки, отсутствия строгой типизации и других факторов, проекты, написанные на PHP, зачастую не блещут качеством и вместо этого содержат множество беспорядочных запросов к базе данных.

красивого, чистого кода.

PHP — это язык сценариев, сервер отвечает на запрос, и объекты умирают. Да, это не десктопное приложение.

Но это не значит, что объекты предметной области, с которыми нам предстоит работать, вообще не нужны.

Наоборот! Они нужны, они должны помочь нам сохранить и восстановить их состояние после того, как они будут удалены из памяти.

На PHP можно и нужно писать качественный код, но это совершенно не зависит от языка! В первую очередь статья будет полезна новичкам, но думаю не помешает и опытным разработчикам.

Возможно, в вашем проекте все не так, как вам хотелось бы? Я занимаюсь разработкой на PHP уже 3 года и меня постоянно беспокоит одна вещь.

Вместо работы с сущностями в коде мы работаем с базой данных.

Хотя это в корне неверно.

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

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

Везде одно и то же.

В некачественных проектах зачастую понятия «Модель» вообще нет, есть только запутанная логика, в коде которой что-то выдергивается из базы, потом огромная куча циклов и условий и потом данные попадают в шаблон.

Более того, по-прежнему существует множество приложений с голыми MySQL-запросами, в лучшем случае через обертку над PDO. В проектах более высокого качества используется ORM, но это не то, с чем вам хотелось бы работать для реализации определенных функций.

В любом случае, чтобы что-либо сделать, нужно заглянуть в «Модель», посмотреть соединения или запустить DESC/EXPLAIN в консоли с подключенной базой данных.

В результате код приложения не скрывает никаких операций с данными в методах сущности.

А код, где должна быть простая (или не очень) бизнес-логика, пестрит строками типа Orm::find. Эта путаница очень разочаровывает. Особенно когда проект большой и переписать его невозможно.

Максимум — одновременно внедрять новый код и работать с ним, а со временем потихоньку отходить от старого.

После прочтения замечательной книги Рика Вэнса «Domain-Driven Design» (ссылка в конце статьи), или Domain-Driven Design, мне пришла в голову мысль, что разработка PHP по сути сводится к выдергиванию строк из базы данных.

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

Мы не должны обходить агрегат и лезть в его подчиненные объекты, и точно так же мы не должны «писать запросы» вместо работы с объектами.

Объекты должны иметь возможность сохранять свое состояние, а также восстанавливаться из базы данных.

Для сложных объектов и агрегатов можно использовать фабрики.

Но ни в коем случае нельзя строить всю логику на MySQL-запросах.

Однако все проекты, в которых мне приходилось работать, злостно нарушают это архитектурное правило.

Позвольте мне привести вам очень простой пример: Клиент - объект сущности «Клиент» Заказ – агрегатный объект «Заказ» Единица заказа – объект неизменяемого значения «OrderItem».

У клиента может быть много заказов; один заказ может состоять из многих единиц.

Единица заказа является неизменяемой величиной, поскольку в случае изменения цены товара (а также скидки от клиента) или удаления товара из магазина история заказов должна быть достоверной и содержать данные, которые было актуально на момент покупки.

Неправильно просто ссылаться на продукт. И так, вам нужно написать в методе/контроллере API простую логику, которая должна отображать заказ пользователя.

Код намеренно упрощен и показывает только суть.

В данном случае не имеет значения, как пришел id, и как мы авторизуем пользователя.

Опция 1:

 
 $order = Orm::find('Order', [ ['id', $id], ['user_id', $user->getId()] ]);
 if (!empty($order)) {
   $items = Orm::find('OrderItem', [ ['order_id', $id] ]); 
   return $items;
 } else {
   return 'Order was not found';
 }
 
Вариант 2:
 
 $order = $user->getOrder($id);
 return $order->getItems();
 
В первом случае мы заходим в базу данных и извлекаем заказ с запрошенным идентификатором от текущего пользователя, чтобы не выдать чужой заказ, затем идем в базу данных и получаем из таблицы OrderItem записи, которые связаны с этим заказом.

Далее вам, скорее всего, понадобится «перебрать» результат с помощью foreach/array_walk/array_map, чтобы привести данные к нужному виду.

Этот код, несомненно, работает, но быстро обрастает однотипным кодом и дублируется в разных частях проекта.

Подобные вызовы полностью разрушают построенную архитектуру проекта и делают его просто бессмысленным.

Первую версию кода гораздо сложнее поддерживать, и ее не так легко читать, как вторую версию.

Orm::find с не всегда такими очевидными параметрами и ненужным условием.

В случае каких-то изменений в хранении заказов в базе данных нам придется в лучшем случае искать по всему коду и редактировать вызовы ORM. Во втором случае мы имеем четко определенные сущности с удобным интерфейсом.

В методе getOrder Логика проверки связи заказ-пользователь уже встроена, в этом случае, например, будет выброшено Exception. И в методе getItems У нас уже есть все необходимое, чтобы просто вернуть список позиций.

Читая этот код, сразу становится понятно, что он на самом деле делает. Кроме того, такой код легче тестировать.

Можно даже написать все в одну строку:

 return $user->getOrder($id)->getItems();
Выводы: В одном из своих проектов, который начал писать другой программист, в ходе разработки внедряются модели, которые не только инкапсулируют работу с базой данных, но и скрывают недостатки ее архитектуры и неявные имена полей.

Пишите код так, чтобы вам было приятно работать в дальнейшем и его было легко поддерживать.

Раз уж мы пишем код с использованием ООП, давайте поработаем с объектами и воспользуемся всеми преимуществами этой парадигмы.

Бизнес-логика, которую мы описываем в проектах, основана на сущностях, а не на выборках и массивах MySQL. Не усложняйте жизнь себе и другим! Не ленитесь написать класс, описывающий сущность там, где это необходимо, не ленитесь написать метод, который будет вам полезен, копипаста — это зло.

А с набором готовых сущностей и готовых методов последующая разработка, рефакторинг и тестирование упростятся и ускорятся! P.S.: Статья просто пища для размышления.

Ссылка на книгу: ЭRik ЭVans — проектирование, ориентированное на предметную область Теги: #дизайн #ООП #ddd #php #качество кода #php #Системный анализ и проектирование

Вместе с данным постом часто просматривают: