Объединить домен с активной записью

Я использую yii2 и считаю, что это очень удобная активная запись.

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

И я просмотрел несколько книг, в большинстве из них предлагается использовать преобразователь данных для сопоставления записи базы данных с доменом.

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

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

class UserModel extends ActiveRecord{
      // do database operations
}

class UserDomain extends UserModel{
    // do domain's logic
}

Не знаю, великолепен ли этот дизайн? Скажите, пожалуйста, ваши предложения.

обновление # 1

class UserDomain {
    private $model;

    public function __construct(UserModel $model){
         $this->model=$model;
    }

    public function __set($name, $value){
         if (isset($this->model->$name)) {
             $this->model->$name=$value;
         } else {
             $this->$name=$value;
         }
    }

    public function __get($name){
         if (isset($this->model->$name)) {
             return $this->model->$name;
         } else {
             return $this->$name;
         }
    }
}

person Jack    schedule 17.04.2017    source источник


Ответы (1)


Отдельный уровень домена и данных

Подход, который вы предполагаете, определенно лучше, чем написание всей бизнес-логики в классе ActiveRecord. Это сделает ваш код понятным и понятным. На мой взгляд, упускается одна вещь - гибкость. Основная идея:

Классы домена должны использовать классы реализации, а не наследовать от них.

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

Простой пример показывает, как заменить наследование композицией:

class UserDomain {

    private $model;

    public function __construct(UserModel $model)
    {
        $this->model = $model;
    }
}

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

Yii2 использует шаблон Dependency Injection, который поможет вам контролировать такие зависимости. Вот ссылка на официальные документы с примером использования контейнера для внедрения зависимостей. Вкратце, вы можете создавать экземпляры своего класса домена следующим образом:

$user = $container->get('UserDomain');

И Yii внедрит для вас все необходимые зависимости.


Доступ к уровню данных

Другой вопрос касается доступа к уровню данных из домена. Я почти уверен, что использование магических методов PHP - это плохо идея.

В классе UserDomain вы должны использовать методы более высокого уровня, в отличие от класса UserModel. Итак, обычно у вас нет setEmail() метода на уровне домена, и вместо этого используйте updateProfile().

class UserDomain
{

    public function updateProfile(string $name, string $email)
    {
        $this->model->name = $name;
        $this->model->email = $email;
        $this->model->save();
    }

}

Если важно использовать атрибуты, как вы предполагаете в комментарии, я бы предпочел использовать Yii-реализацию Свойства. Для электронного письма код будет выглядеть так:

 /**
 * Class UserDomain
 *
 * @property string email
 */
class UserDomain extends \yii\base\Object
{

    public function setEmail(string $email)
    {
        $this->model->email = trim($email);
    }

}

$userDomain = new UserDomain();
$userDomain->email = '[email protected]';

Обратите внимание, что UserDomain расширен с \yii\base\Object, чтобы сделать сеттер доступным.


Автоматическая установка атрибутов ActiveRecord

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

Это то, для чего был разработан ActiveRecord. Так что это будет удобно и оптимально, пока слой вашего домена остается простым и понятным.

person wormi4ok    schedule 17.04.2017
comment
У меня есть вопрос, используя композицию, мне нужно написать код сопоставления для сопоставления атрибутов ActiveRecord с атрибутами домена, которые, как мне кажется, знакомы с сопоставителем данных ... Или я могу использовать магическую функцию php '__set' и '__get' в домене, установить и получить атрибуты из ActiveRecord этим способом? Пример кода находится в моем сообщении выше в обновлении №1. - person Jack; 17.04.2017
comment
Нет, использование волшебной функции может вызвать неожиданное поведение. Я обновлю ответ, чтобы объяснить себя. - person wormi4ok; 17.04.2017
comment
эм ... Мой вопрос в том, как автоматически сопоставить атрибуты ActiveRecord с атрибутами домена ... IoC просто помогает вам создать объект с его зависимостью, но как я могу автоматически установить значение ActiveRecord ... Например, если я хочу изменить пользователя электронной почты, я могу использовать $uesr_domain->email="[email protected]";, чем я хочу сохранить его в базе данных, как я могу установить для этого атрибута $model? - person Jack; 17.04.2017
comment
Да, ты прав, @ Джек, моя ошибка. Я просто хотел предостеречь вас от использования неправильной архитектуры. Но, похоже, вы приняли правильное решение. С наилучшими пожеланиями! - person wormi4ok; 17.04.2017
comment
Да, я получил его. Большое спасибо за ваше терпение, и я узнаю больше из этого обсуждения. С наилучшими пожеланиями! - person Jack; 17.04.2017
comment
проверить поведение yiiframework.com/doc-2.0/guide-concept-behaviors .html - person e-frank; 18.04.2017
comment
это может показать вам DI в действии http://stackoverflow.com/questions/38582341/yii2-configurable-models-inside-module/38591495#38591495 - person e-frank; 18.04.2017