Модели доменов PHP, DAO и способы их реализации

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

Чтобы использовать очень простой пример, примерно так:

class User {

    private $name;

    public setName() {}
    public getName() {}

}

Теперь возникает вопрос, как я могу заполнить эту "модель домена" либо из какого-либо источника ввода, либо из базы данных или источника данных?

Во время чтения о моделях доменов у меня создалось впечатление, что внутри них должно быть не более чем представление рассматриваемой концепции домена. Итак, теперь мне также понадобится другой класс (DAO?), Ответственный за взаимодействие с источником данных, в данном случае таблицу базы данных с именем "Пользователь". Мой класс DAO будет обрабатывать вставки, обновления, удаления и множественные выборки.

Я придумал этот подход для заполнения пользовательской модели домена с помощью ввода, в этом случае постданных, а затем сохранения записи в базе данных с помощью класса UserDAO:

/**
 * Populating Domain Model from input, and saving to database
 */

/** Instantiate User Domain Model, and populate it from input */
$user = new User();
$user->setName($_POST['name']);

/** Creating a new database record from User Domain Model */
$userdao = new UserDAO($pdo);
$userdao->insert($user);

И вот как я ожидал взаимодействия с базой данных, когда мне нужно получить данные, в этом случае несколько записей пользователя:

/**
 * Fetching data from database to populate User Domain Models in an array
 */

/** Instantiate new UserDAO object to interact with User table */
$users = new UserDAO($pdo);
$users->findAll();

$user_collection = [];

/** Loop UserDAO object set to populate user collection array */
foreach ($users as $user) {

    /** Create new User domain object from row, and add to collection array */
    $user = new User($user);
    $user_collection[$user->name()] = $user;

}

Кажется, что единственной реальной выгодой здесь является организация.

Моя текущая итерация по существу имеет класс User, который берет на себя все обязанности класса UserDAO выше и сразу же возвращает массивы данных из базы данных, которые затем я использую в своих "Контроллерах" / "Докладчиках" и которые стекают через мои (пассивные) взгляды.

Мне интересно:

  • Я на правильном пути?

  • Где применяется проверка ввода? Я предполагаю, что он должен идти в Domain Model, если я прав в своих предположениях до сих пор?

  • В чем преимущество использования этого метода, кроме помощи в организации базовых концепций, на которые будет опираться и работает приложение? Зачем мне этот дополнительный слой вместо того, чтобы напрямую работать с результатами массива из базы данных?

Ответ 1

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

Объект пользовательского домена

Должна быть ответственна за то, что рассказала о своем состоянии в отношении полезных бизнес-правил вашего приложения (isAdmin(), isBanned(), isSuspended(), getWarnLevel()). Подумайте об этом объекте, таком как ведро API. Что вы хотите знать об этом? Какую полезную информацию он может рассказать? Создайте API, который отвечает на эти вопросы. Будьте осторожны, чтобы пользователь не сказал вам слишком много о ДРУГИХ объектах в системе. Это не должно быть его ответственностью.

Заботится о:

  • Рассказывая о себе
  • Управление собственным состоянием

Не волнует

  • Сохраняется ли он.
  • Как это сделано
  • Любые другие объекты (если только это не агрегированный корень)
  • Много других вещей

Репозиторий пользователя

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

(findById($id), findByEmail($email), findBanned(), findByCriteria($Criteria) - хороший кандидат для стратегии или шаблона спецификации, save($User), delete($User)). Опять же, ключевым моментом здесь является создание API, который удовлетворяет бизнес-правилам домена. Вам нужно найти пользователя по электронной почте? Затем сделайте эту явную точку доступа в репозитории. Если у вас нет необходимости, то нет. Как вам нужно найти Users? Ответьте, что с API.

Заботится о

  • Предоставление вам доступа к существующим объектам пользователя на основе любых произвольных критериев
  • Вызов механизма сохранения, который вы ему предоставили

Не волнует

  • Как точно сохраняются объекты User
  • Создание объектов User
  • Много других вещей

Пользователь Factory

UserRepositories предназначены для обработки существующих объектов User, но как вы их создаете в первую очередь? С заводами. Это могут быть простые фабрики, которые просто делают одного типа пользователей. Или они могут быть абстрактными фабриками, которые создают разные типы пользователей. Это зависит от вас и от ваших системных потребностей. Опять же, подумайте о том, какой API необходим для удовлетворения бизнес-правил вашего домена: (make($type, $data), makeAdmin($data), makeMember($data)). PHP 5.6 variadic синтаксис оператора заставит этот очиститель WAY работать с btw.

Заботится о

  • Создание блестящего нового Users

Не волнует

  • Источником данных для создания этих блестящих новых Users
  • Что вы делаете с User после его создания.
  • Много других вещей

User Gateway/Mapper

Это может быть ваш фактический механизм сохранения: он может напрямую взаимодействовать с реляционной базой данных, использовать Factory и использоваться репозиторием. Он выполняет фактическую выборку БД и отображает данные в формат, который может переварить Factory. Factory не должен нести ответственность за это сопоставление, потому что он не должен иметь никакого знания исходного формата, только формат, необходимый ему для сборки объекта домена. Таким образом, ответственность за отображение лежит на Gateway/Mapper.

Заботится о

  • Где User сохраняется или извлекается, и как
  • Перевод этих данных из персистенции на что-то Factory хочет
  • Наверное, заботится о Factory

Не волнует

  • Конкретный драйвер хранилища (например, MySQL, Postgres) - это то, где PDO входит
  • Создание нового объекта User
  • Много других вещей

Теперь, по общему признанию, это выглядит намного проще, чем на самом деле. Как вы обрабатываете дочерние агрегаты (например, много Comments, принадлежащих одному Post)? На какой стадии вы даете их Post? Вы даже даете им это все время или только когда вы явно запрашиваете его (например, через обратный вызов?). Это сложные вопросы, на которые у меня нет ответов, и на которые частично отвечают ваши потребности в домене в на первом месте.

ORM - сложная проблема. Доктрина и Красноречивость превосходны, но не следует строго следовать приведенной выше схеме. Это нормально. Образец выше - это руководство, а не правило. Цель состоит в том, чтобы сосредоточиться на разделении проблем и сосредоточенной ответственности. Возможно, для вашего приложения не обязательно иметь все эти слои.

Что касается проверки, существует два вида проверки:

  • Проверка ввода формы

  • Проверка объектов домена

Валидация формы обычно лучше всего выполняется с помощью класса валидатора формы и некоторых правил, определенных для данной формы. Обычно вы можете определить эти правила в любом классе строителя формы, который у вас есть (например, Form::text('first_name', array('rules' => 'required|alpha'))). Контроллер должен принимать валидатор формы как зависимость, но не должен выполнять собственно проверку.

Проверка сборки объектов домена (например, защита целостности вашей модели) может жить либо в самом объекте домена через сеттеры, либо жить в factory. Это полностью зависит от того, как вы планируете создавать объекты домена.

Следует отметить, что у вас должны быть ОБА типы проверки: проверка формы для проверки ввода и проверка объектов домена для проверки целостности данных при создании объекта.