UoW

Обсуждаем, как правильно строить приложения
Ответить
Аватара пользователя
BrusSENS
Сообщения: 565
Зарегистрирован: 2012.07.26, 06:51
Откуда: Новороссийск
Контактная информация:

UoW

Сообщение BrusSENS »

Всем привет. Есть вопрос, касательно реализации.
Профит от использования понятен. Удобно вносить изменения, есть возможность закоммитить или откатить изменения, сущности в IdentityMap не изменяются, пока мы не закоммитим их после реального сохранения измененной сущности. Но вот как всё таки лучше реализовывать? Видел в сети варианты с реализацией, как отдельного объекта от сущности, в Yii в AR моделях есть похожая реализация, но всю работу на себя берёт сама сущность. Вроде такой вариант удобен, но вертикальное расширение кажется сомнительным вариантом. Насколько верно запихивать возможности UoW в сущность?
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: UoW

Сообщение zelenin »

нинасколько
Аватара пользователя
BrusSENS
Сообщения: 565
Зарегистрирован: 2012.07.26, 06:51
Откуда: Новороссийск
Контактная информация:

Re: UoW

Сообщение BrusSENS »

zelenin писал(а): 2017.09.07, 11:38 нинасколько
Понятно.
Вот вроде как профит от использования понятен, но что то не могу понять, как тогда отдельным объектом это реализовать?
Как я понимаю, если есть у нас изменения сущности, то мы добавляем эти изменения в UoW, после чего, если у нас в хранилище изменились данные сущности, то коммитим изменения. Но вот тут вопрос, каким тогда образом их в сущности самой фиксировать? Есть у сущности некие методы, вроде changeEmail(string $email), которые генерируют некие события. Получается, что если сущность ничего не знает о UoW, то и UoW особо не будет знать какой метод вызвать для изменения некоего свойства. Не поделитесь опытом реализации данного паттерна?
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: UoW

Сообщение zelenin »

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

interface UnitOfWork
{
    /**
     * @param object $entity
     *
     * @return bool
     */
    public function exists($entity): bool;
    /**
     * @param object $entity
     */
    public function registerNew($entity);
    /**
     * @param object $entity
     */
    public function registerClean($entity);
    /**
     * @param object $entity
     */
    public function registerDirty($entity);
    /**
     * @param object $entity
     */
    public function registerDeleted($entity);
    public function commit();
    public function clear();
}
что-то такое и реализация под конкретное хранилище, где в commit мы сохраняем в него
Аватара пользователя
BrusSENS
Сообщения: 565
Зарегистрирован: 2012.07.26, 06:51
Откуда: Новороссийск
Контактная информация:

Re: UoW

Сообщение BrusSENS »

zelenin писал(а): 2017.09.07, 13:20 реализация под конкретное хранилище
Под конкретных хранилищем Вы имеете ввиду репозиторий (ProductRepository) или конкретную реализацию репозитория (SQLProductRepository)?
zelenin писал(а): 2017.09.07, 13:20 где в commit мы сохраняем в него
Получается, что, например в коммандах, работаем непосредственно с UoW, не зная о самом репозитории?
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: UoW

Сообщение zelenin »

скорее это то, что внутри репозитория. UoW - это прикладной уровень, он не может быть над хранилищем.
под хранилищем я имею в виду конкретный адаптер, типа PDO или DbConnection или entity manager - то, что вы используете внутри репозитория.

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

    public function save(Post $post) {
        $this->unitOfWork->registerDirty($post); // например реализая PdoUnitOfWork
        $this->unitOfWork->commit($post);
    }
но в такой реализации смысла не очень много, если конкретный кейс сохраняет одну сущность. Вижу смысл только в не-репо архитектуре либо в реализации своей ORM/Entity Manager.

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

Re: UoW

Сообщение zelenin »

UoW пригодится для долго работающих приложений, где мы можем накапливать сущности в UoW, и потом всей пачкой закоммитить в хранилище. Но в случае репо мы должны закоммитить сразу. итого весь наш рабочий процесс будет выглядеть так (представим случай покупки и авторегистрации при покупке):
OrderService::createOrder
- добавляем Order в OrderRepository (внутри добавляем Order в UoW и сразу коммитим)
- добавляем User в UserRepository (внутри добавляем User в UoW и сразу коммитим)

Выгоды ноль.
Можно модифицировать репозитории, чтобы они не коммитили, а на уровне приложения повесить хук на завершение, который будет коммитить все накопившееся, но это грязновато.
Аватара пользователя
BrusSENS
Сообщения: 565
Зарегистрирован: 2012.07.26, 06:51
Откуда: Новороссийск
Контактная информация:

Re: UoW

Сообщение BrusSENS »

zelenin писал(а): 2017.09.07, 14:18 Зачем он вам?
Спасибо за разъяснения. Видимо я не верно понял смысл данного паттерна.
Собственно для чего я хотел использовать данный паттерн:
Есть у нас IdentityMap, из которого, при наличии, мы вытаскиваем уже готовые сущности. Так вот, как мне кажется верным, сущность до реального сохранения в хранилище (я о MySQL, XML и т.п.) не должна изменяться. А уже после сохранения изменений применять изменения в самой сущности.
Вообще Вы как считаете? Должна ли сущность изменяться до сохранения в хранилище? Например изменять свой статус через changeStatus() моментально, или всё таки нужно эти изменения где-то зафиксировать, а только после сохранения отобразить их в сущности.
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
Аватара пользователя
BrusSENS
Сообщения: 565
Зарегистрирован: 2012.07.26, 06:51
Откуда: Новороссийск
Контактная информация:

Re: UoW

Сообщение BrusSENS »

Т.е. реальный пример кода:

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

// Моментально применяем изменения
class User
{
    private $email;
    public function changeEmail(string $email)
    {
        // Проверяем, не равнозначны ли старый и новый E-mail
        $this->email = $email;
        $this->flashEvent(new UserChangeEmail($this));
    }
}
// ИЛИ
// применяем только после сохранения
class User
{
    private $email;
    
    private $dirtyAttributes = [];
    
    public function changeEmail(string $email)
    {
        // Проверяем, не равнозначны ли старый и новый E-mail
        $this->markDirty('email', $email);
        $this->flashEvent(new UserChangeEmail($this));
    }
    
    public function commitChanges()
    {
        foreach($this->dirtyAttributes as $attributeName => $newValue) {
            $this->$attributeName = $newValue;
        }
    }
}
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: UoW

Сообщение zelenin »

не совсем понимаю, но расскажу как это должно работать на примере реалихации EntityManager, работающей с IM и UoW.

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

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

// проверяем есть ли сущность в IM. ее нет - достаем из базы, кладем в IM, регистриуем в UoW как clean
$post = $entityManager->find(Post::class, $postId);
// меняем статус сущности
$post->activize();
// в IM сущность уже обновленная (по ссылке), в UoW ее перерегистриуем как dirty.
$entityManager->store($post);
Дальше во втором сервисе:

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

// проверяем есть ли сущность в IM. она есть  - отдаем из IM, запроса в базу не происходит. В UoW она уже помечена
$post = $entityManager->find(Post::class, $postId);
// добавляем коммент
$post->addComment($comment);
// в IM сущность уже обновленная (по ссылке), в UoW ее перерегистриуем как dirty (она уже dirty - не суть).
// тут же помечается Comment как new
$entityManager->store($post);
И вот теперь то, для чего UoW нужен

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

// в конце реквеста коммитим - внутри вызывается UoW::commit
$entityManager->commit();
Итого у нас два кейса с сохранением сущностей, но с одним оптимальным взаимодействием с базой (например если создано два поста, то они вставятся одним инсертом)

В случае с репозиториями у нас будет два кейса - два коммита, ноль профита.
Последний раз редактировалось zelenin 2017.09.07, 16:44, всего редактировалось 1 раз.
Аватара пользователя
BrusSENS
Сообщения: 565
Зарегистрирован: 2012.07.26, 06:51
Откуда: Новороссийск
Контактная информация:

Re: UoW

Сообщение BrusSENS »

zelenin писал(а): 2017.09.07, 16:08 Итого у нас два кейса с сохранением сущностей, но с одним оптимальным взаимодействием с базой (например если создано два поста, то они вставятся одним инсертом)

В случае с репозиториями у нас будет два кейса - два коммита, ноль профита.
Всё, понял) Спасибо)
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
Ответить