Аггрегат и коллекция

Обсуждаем, как правильно строить приложения
Ответить
noLogicOnlyWar
Сообщения: 75
Зарегистрирован: 2017.07.04, 20:53

Аггрегат и коллекция

Сообщение noLogicOnlyWar » 2017.12.22, 21:14

Есть аггрегат пусть будет класс X, он содержит коллекцию сущностей Y. Требуется реализовать перенос сущности Y из одного экземпляра X в другой X. При этом перед переносом нужно проверить ряд параметров, тк Y'у может быть запрещен переход в контекст нового X.

Примерный код:

Код: Выделить всё

class X {
    public function moveYTo(X $x, Y $yToTransfer)
    {
        //добавляем к новому аггрегату
        $x->addY($yToTransfer);
        //удаляем из текущего
        unset($this->ys[$yToTransfer]); //тут конечно не $yToTransfer а ключ
    }

    private function addY(Y $y)
    {
        //проверяем может ли y существовать в новом контексте
        $this->failIfContextWrong($this, $y);
        $y->changeOwnerId($this->id);
        $this->ys[] = $y;
    }
}

class Y {
    private $ownerId;
    public changeOwnerId($id)
    {
        $this->ownerId = $ownerId;
    }
}
Смущает changeOwnerId(). Нужно сменить связь, но не хочется чтобы метод changeOwnerId был public, ибо ктонибудь другой может посмотреть на код и решить что достаточно вызвать changeOwnerId чтобы переместить Y. В cpp я так понимаю это можно решить через friend's классы. Как решаете такие ситуации? Возможно есть какой то способ обойти сию проблему?

Аватара пользователя
samdark
Администратор
Сообщения: 8811
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Аггрегат и коллекция

Сообщение samdark » 2017.12.23, 00:13

1. Скорее всего не нужно в Y onwerId. Сам факт нахождения в аггрегате на это указывает.
2. Если всё-таки надо, Y можно сделать immutable:

Код: Выделить всё

class Y
{
    private $ownerId;

    public function __construct($ownerId, ...)
    {
        $this->ownerId = $ownerId;
    }
}
Тогда X будет таким:

Код: Выделить всё

class X
{
    private $ys = [];

    public function move(Y $what, X $where)
    {
        $newY = new Y($where->id, $what->something, ...);
        $where->add($newY);
        $this->remove($what);
    }

    private function add(Y $y)
    {    
        $this->ys[] = $y;
    }
}

noLogicOnlyWar
Сообщения: 75
Зарегистрирован: 2017.07.04, 20:53

Re: Аггрегат и коллекция

Сообщение noLogicOnlyWar » 2017.12.23, 12:26

Спасибо за ответ.
Скорее всего не нужно в Y onwerId. Сам факт нахождения в аггрегате на это указывает
Не уверен что можно обойтись без какой либо ссылки на owner, ведь зачастую нам надо будет получить аггрегат зная только id Y. Например в сервис приложения приходит реквест на update y, наши действия в этом случае будут: достать из репо y, достать из репо x через y->owner->id, вызвать x->updateY(...). И даже если разрулить такие моменты - орм все равно необходима будет ссылка на owner.
Если всё-таки надо, Y можно сделать immutable
Тоже над этим думал, но запутался в вопросах:
id нужно сохранять тк на Y->id могут быть завязаны вещи в совершенно другом месте (например где-то хранится статистика), соответственно мы должны отправить в конструктор Y id из старого Y ? Это вообще законно? id по определению уникально для сущности а мы выходит таскаем его от одной к другой... с теоретической стороны это недопустимо? или ок? К тому же иммутабельность выйдет сомнительная, ведь id у y_old и y_after_move будет одинаковый а остальные поля не факт, значит с точки зрения домена это 1 мутабельный объект?

zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Аггрегат и коллекция

Сообщение zelenin » 2017.12.23, 16:50

noLogicOnlyWar писал(а):
2017.12.23, 12:26
Спасибо за ответ.
Скорее всего не нужно в Y onwerId. Сам факт нахождения в аггрегате на это указывает
Не уверен что можно обойтись без какой либо ссылки на owner, ведь зачастую нам надо будет получить аггрегат зная только id Y. Например в сервис приложения приходит реквест на update y, наши действия в этом случае будут: достать из репо y, достать из репо x через y->owner->id, вызвать x->updateY(...). И даже если разрулить такие моменты - орм все равно необходима будет ссылка на owner.
она будет. вы же сделаете $x->add($y); дальше orm при сохранении сама все разрулит. Двойная связь ни к чему.
В вашем случае пришел id Y, по нему сразу нашли агрегат X, и дальше с ним работаете.

noLogicOnlyWar
Сообщения: 75
Зарегистрирован: 2017.07.04, 20:53

Re: Аггрегат и коллекция

Сообщение noLogicOnlyWar » 2017.12.23, 18:16

zelenin писал(а):
2017.12.23, 16:50
она будет. вы же сделаете $x->add($y); дальше orm при сохранении сама все разрулит. Двойная связь ни к чему.
В вашем случае пришел id Y, по нему сразу нашли агрегат X, и дальше с ним работаете.
Спасибо, тогда немного конкретики, как это сделать в доктрине? судя по докам только как One-To-Many, Unidirectional with Join Table ? что для меня не очень хорошая новость, тк хотелось бы замапить на уже существующую структуру бд, в которой у таблицы Y просто есть поле owner_id

zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Аггрегат и коллекция

Сообщение zelenin » 2017.12.23, 18:30

noLogicOnlyWar писал(а):
2017.12.23, 18:16
zelenin писал(а):
2017.12.23, 16:50
она будет. вы же сделаете $x->add($y); дальше orm при сохранении сама все разрулит. Двойная связь ни к чему.
В вашем случае пришел id Y, по нему сразу нашли агрегат X, и дальше с ним работаете.
Спасибо, тогда немного конкретики, как это сделать в доктрине? судя по докам только как One-To-Many, Unidirectional with Join Table ? что для меня не очень хорошая новость, тк хотелось бы замапить на уже существующую структуру бд, в которой у таблицы Y просто есть поле owner_id
а какая структура сейчас?

noLogicOnlyWar
Сообщения: 75
Зарегистрирован: 2017.07.04, 20:53

Re: Аггрегат и коллекция

Сообщение noLogicOnlyWar » 2017.12.23, 18:50

Таблица X, таблица Y с полем x_id для связи

zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Аггрегат и коллекция

Сообщение zelenin » 2017.12.23, 19:13

noLogicOnlyWar писал(а):
2017.12.23, 18:50
Таблица X, таблица Y с полем x_id для связи
http://docs.doctrine-project.org/projec ... irectional
то, что это bidirectional, не значит что надо обязательно поддерживать двухстороннюю связь, предоставляя Y публичное апи для добавления X. Доктрина сама добавит через рефлексию. Двухсторонняя связь нужна, если вы будете работать с Y в рамках того же реквеста, но т.к. вы с Y работаете только через X (агрегат), то связь не нужна.

noLogicOnlyWar
Сообщения: 75
Зарегистрирован: 2017.07.04, 20:53

Re: Аггрегат и коллекция

Сообщение noLogicOnlyWar » 2017.12.23, 20:59

Если я вас правильно понял то должен работать такой вот код :
$y = $x1->ys->get(0);
$x1->ys->remove(0);

$x2->ys->add($y);

$em->flush();
Но он не работает, выполняется как ожидаю только если добавить перед флашем $y->changeOwner($x);
Настройки мапинга дефолтные из дока, за исключением того что orphanRemoval: false

zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Аггрегат и коллекция

Сообщение zelenin » 2017.12.23, 21:19

да, возможно не будет работать. что впрочем тоже можно объяснить, ведь схема составлена несогласно доменному слою - получается не one X владеет many Y, а many Y владеют one X. То есть агрегат - Y, хотя должен быть X. То есть у вас в домене связь в одном направлении, а в хранилище в другом.

http://docs.doctrine-project.org/projec ... irectional

noLogicOnlyWar
Сообщения: 75
Зарегистрирован: 2017.07.04, 20:53

Re: Аггрегат и коллекция

Сообщение noLogicOnlyWar » 2017.12.23, 21:53

Хм, да, так и есть, спасибо большое что прояснили.
Теоретически, в контесте разрабатываемого модуля, я думаю можно рассматривать и Y как агрегат, то есть множество Y находятся в одном контексте X. Тогда задаче по смене владельца будет решаться $y->changeOwner($newContext) и вся логика тогда уйдет в этот метод. Как думаете вывернуть вывернуть так доменную логику? Или все же способ выше это все в угоду инфраструктуры, domain first и мигрировать структуру бд?

zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Аггрегат и коллекция

Сообщение zelenin » 2017.12.23, 22:07

noLogicOnlyWar писал(а):
2017.12.23, 21:53
Хм, да, так и есть, спасибо большое что прояснили.
Теоретически, в контесте разрабатываемого модуля, я думаю можно рассматривать и Y как агрегат, то есть множество Y находятся в одном контексте X. Тогда задаче по смене владельца будет решаться $y->changeOwner($newContext) и вся логика тогда уйдет в этот метод. Как думаете вывернуть вывернуть так доменную логику? Или все же способ выше это все в угоду инфраструктуры, domain first и мигрировать структуру бд?
такое нормально, что член одного агрегата может быть корнем другого агрегата. но в данном случае кажется, что вы именно в угоду зранилищу меняете.

Ответить