CRUD в DDD

Обсуждаем, как правильно строить приложения
Ответить
Faeron
Сообщения: 9
Зарегистрирован: 2017.10.14, 19:39

CRUD в DDD

Сообщение Faeron » 2017.10.14, 19:51

Доброго времени суток.

Подскажите, пожалуйста, как быть с CRUD операциями при DDD подходе.

В частности имеется API-сервис.
Сущность "камера":
1. id
2. name
3. description
4. username
5. password
6. Location (Entity)
7. Status (Entity)
8. AccessType (Entity)
...
и еще довольно много атрибутов.

В API есть метод PUT /api/cameras/{id}, который подразумевает замену множества полей.

Правильно ли я понимаю, что алгоритм будет следующим
HTTP Request -> Controller -> Mapper (Body to DTO) -> Application Service -> (поиск модели в репозитории, вызов методов модели changeName, changeDescription, changeUsername, и т.д.)?

Если так, то чем это отличается от setter'ов?

Какие еще есть варианты?

Спасибо!

Аватара пользователя
slavcodev
Сообщения: 3133
Зарегистрирован: 2009.04.02, 21:42
Откуда: Altea, Spain
Контактная информация:

Re: CRUD в DDD

Сообщение slavcodev » 2017.10.14, 20:29

> Если так, то чем это отличается от setter'ов?

Сеттерами называют методы которые открывают доступ к смене значения инкапсулированного свойства объекта, без никакой дополнительной логики.

Смена `username` и `password` это change credentials, уж точно такая смена влечет кучу бизнес процессов нужно проверить.
Смена `status` (по названию) я предлагаю что это тоже смена состояния сущности и тоже куча инвариантов при этом надо удовлетворить.

Теоретически, такой метод не является сеттером, т.к. инкапсулирует два инварианта

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

public function setDescription(string $value)
{
   $value = trim($value);
   
   if (empty($value) {
       throw new InvalidArgumentException('Trying to set invalid description');
   }

   $this->description = $value;
}
Если всего этого нет, и тут простой CRUD нужен загрузил данные из БД, сменил свойства и пересохранил, то используйте CRUD.
Не верьте никому кто говорит что CRUD плохой, DDD рулит. Требования к приложению диктуют что юзать а не программисты и их фанатизм.
Жду Yii 3!

Faeron
Сообщения: 9
Зарегистрирован: 2017.10.14, 19:39

Re: CRUD в DDD

Сообщение Faeron » 2017.10.14, 22:02

Спасибо за ответ.

По мимо CRUD, присутствует и бизнес-логика, по этому и вопрос как это совместить.

Faeron
Сообщения: 9
Зарегистрирован: 2017.10.14, 19:39

Re: CRUD в DDD

Сообщение Faeron » 2017.10.27, 10:49

Прошу помочь,

В административном интерфейсе, есть некая сущность, которую можно создавать, удалять, редактировать (CRUD).
При этом интерфейс просмотра сущности, совмещен с редактированием, т.е. большую часть атрибутов сущности маппится на форму для редактирования.

DTO

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

<?php

class Entity extends \yii\base\Model
{
    public $name;
    public $someFiled;
    ...

    public function rules()
    {
        return [
            // валидация
        ];
    }
}
ActiveFrom в шаблоне

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

<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
?>
<?php $form = ActiveForm::begin(); ?>

    <?= $form->field($model, 'name') ?>

    <?= $form->field($model, 'someFiled') ?>

    <?= Html::submitButton('Отправить') ?>
<?php ActiveForm::end(); ?>
Controller

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

?php

namespace app\controllers;

use Yii;
use yii\web\Controller;
use app\models\Entity;

class DefaultController extends Controller
{
    public function actionEntry($id)
    {
        // получаем DTO, которое маппим на форму.
        $entity = $queryBus->handle(new GetEntityById($id));

        // если форму отправили, валидируем и в случае успеха отправляем комманду на изменение сущности
        if ($entity->load(Yii::$app->request->post()) && $entity->validate()) {
            $commandBus->handle(new EntityUpdateCommand($entity));
        }

         return $this->render('entity-update', ['model' => $entity]);
    }
}
Но при таком подходе получается, что при отправке формы будет 2 select'а к БД, сначала получаем DTO в контроллере, потом сущность в обработчике команды.
Есть вариант разнести экшены в контроллере, один будет отрисовывать форму, второй, обрабатывать.

правильно ли я понимаю, как организовать подход?

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

Re: CRUD в DDD

Сообщение zelenin » 2017.10.27, 11:57

Faeron писал(а):
2017.10.27, 10:49
Но при таком подходе получается, что при отправке формы будет 2 select'а к БД, сначала получаем DTO в контроллере, потом сущность в обработчике команды.
ну и? а) 2 разных сервиса делают 2 разных запроса - это нормально б) вопрос вашей реализации БД-слоя - можно кэшировать загруженные сущности в памяти (см. Identity Map)
Faeron писал(а):
2017.10.27, 10:49
Есть вариант разнести экшены в контроллере, один будет отрисовывать форму, второй, обрабатывать.
при любой непонятной фигне разноси экшны.

Nex-Otaku
Сообщения: 809
Зарегистрирован: 2016.07.09, 21:07

Re: CRUD в DDD

Сообщение Nex-Otaku » 2017.10.27, 12:19

Не вижу здесь двух селектов.

Данные из БД извлекаются только здесь

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

$entity = $queryBus->handle(new GetEntityById($id));
Дальше эти данные передаются в другие команды.

Faeron
Сообщения: 9
Зарегистрирован: 2017.10.14, 19:39

Re: CRUD в DDD

Сообщение Faeron » 2017.10.27, 12:29

Nex-Otaku писал(а):
2017.10.27, 12:19
Не вижу здесь двух селектов.

Данные из БД извлекаются только здесь

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

$entity = $queryBus->handle(new GetEntityById($id));
Дальше эти данные передаются в другие команды.
Правильно, но команда это тоже DTO, по этому в обработчике команды, опять происходит select сущности (id берется из команды), которая обновляется данными из команды

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

Re: CRUD в DDD

Сообщение zelenin » 2017.10.27, 12:53

Faeron писал(а):
2017.10.27, 12:29
Nex-Otaku писал(а):
2017.10.27, 12:19
Не вижу здесь двух селектов.

Данные из БД извлекаются только здесь

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

$entity = $queryBus->handle(new GetEntityById($id));
Дальше эти данные передаются в другие команды.
Правильно, но команда это тоже DTO, по этому в обработчике команды, опять происходит select сущности (id берется из команды), которая обновляется данными из команды
тогда называйте $dto, а не $entity. И вообще dto - юзкейс-специфичная вещь, то есть к определенному экшну относится, и получать ее нужно в экшне, а не в шине.

Nex-Otaku
Сообщения: 809
Зарегистрирован: 2016.07.09, 21:07

Re: CRUD в DDD

Сообщение Nex-Otaku » 2017.10.27, 19:11

Faeron писал(а):
2017.10.27, 12:29
Правильно, но команда это тоже DTO, по этому в обработчике команды, опять происходит select сущности (id берется из команды), которая обновляется данными из команды
Вот здесь селект второй раз вызывается?

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

EntityUpdateCommand($entity)
Но зачем? Данные-то уже есть в $entity, зачем их повторно извлекать? В чём смысл? Зачем читать что-то из БД, когда всё уже есть в $entity?

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

Re: CRUD в DDD

Сообщение zelenin » 2017.10.27, 19:20

Nex-Otaku писал(а):
2017.10.27, 19:11
Faeron писал(а):
2017.10.27, 12:29
Правильно, но команда это тоже DTO, по этому в обработчике команды, опять происходит select сущности (id берется из команды), которая обновляется данными из команды
Вот здесь селект второй раз вызывается?

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

EntityUpdateCommand($entity)
Но зачем? Данные-то уже есть в $entity, зачем их повторно извлекать? В чём смысл? Зачем читать что-то из БД, когда всё уже есть в $entity?
потому что нам данные из dto (помним что $entity на самом деле dto?) надо загрузить в сущность, а потом сущность сохранить.

Nex-Otaku
Сообщения: 809
Зарегистрирован: 2016.07.09, 21:07

Re: CRUD в DDD

Сообщение Nex-Otaku » 2017.10.27, 19:27

zelenin писал(а):
2017.10.27, 19:20
Nex-Otaku писал(а):
2017.10.27, 19:11
Но зачем? Данные-то уже есть в $entity, зачем их повторно извлекать? В чём смысл? Зачем читать что-то из БД, когда всё уже есть в $entity?
потому что нам данные из dto (помним что $entity на самом деле dto?) надо загрузить в сущность, а потом сущность сохранить.
Ну и грузи их все из $entity, где тут необходимость что-то ещё селектить?

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

Re: CRUD в DDD

Сообщение zelenin » 2017.10.27, 19:29

Nex-Otaku писал(а):
2017.10.27, 19:27
zelenin писал(а):
2017.10.27, 19:20
Nex-Otaku писал(а):
2017.10.27, 19:11
Но зачем? Данные-то уже есть в $entity, зачем их повторно извлекать? В чём смысл? Зачем читать что-то из БД, когда всё уже есть в $entity?
потому что нам данные из dto (помним что $entity на самом деле dto?) надо загрузить в сущность, а потом сущность сохранить.
Ну и грузи их все из $entity, где тут необходимость что-то ещё селектить?
из $entity (которая dto - еще раз) в $entity (которая уже $entity)

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

$commandBus->handle(new EntityUpdateCommand($dto)); // вот тут из $dto мы грузим в $entity, которую нужно достать.

anton_z
Сообщения: 354
Зарегистрирован: 2017.01.15, 15:01

Re: CRUD в DDD

Сообщение anton_z » 2017.10.28, 04:12

Два SELECT тут неизбежны - так как используются шины, которые фактически разделили контексты. В приведенном примере в одном контексте данные читаются, в другом пишутся (при записи нужно сначала проверить, есть ли такой объект в хранилище, поэтому требуется второй SELECT). Есть еще контекст контроллера - там у вас только dto. Без демонтажа шин и объединения их контекстов проблему двух SELECT не решить. Тут уже вам выбирать, что вам дороже - еще один roundtrip к БД или система шин. Чем, кстати, обосновано их применение в проекте? Шина, это, в общем-то EAi (Enterprise Application Integration) паттерн, вашей системе это действительно требуется и приносит пользу? Если начинаются такие вопросы, это индикатор того, что шины в проекте могут быть не к месту.

Nex-Otaku
Сообщения: 809
Зарегистрирован: 2016.07.09, 21:07

Re: CRUD в DDD

Сообщение Nex-Otaku » 2017.10.28, 09:59

zelenin писал(а):
2017.10.27, 19:29
Nex-Otaku писал(а):
2017.10.27, 19:27
Ну и грузи их все из $entity, где тут необходимость что-то ещё селектить?
из $entity (которая dto - еще раз) в $entity (которая уже $entity)

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

$commandBus->handle(new EntityUpdateCommand($dto)); // вот тут из $dto мы грузим в $entity, которую нужно достать.
Ну а зачем её "доставать" и селектить из базы, если все её данные уже есть в DTO? Просто берёшь и создаёшь entity по полям DTO, и всё - к базе обращаться не нужно.

Faeron
Сообщения: 9
Зарегистрирован: 2017.10.14, 19:39

Re: CRUD в DDD

Сообщение Faeron » 2017.10.28, 13:05

anton_z писал(а):
2017.10.28, 04:12
Два SELECT тут неизбежны - так как используются шины, которые фактически разделили контексты. В приведенном примере в одном контексте данные читаются, в другом пишутся (при записи нужно сначала проверить, есть ли такой объект в хранилище, поэтому требуется второй SELECT). Есть еще контекст контроллера - там у вас только dto. Без демонтажа шин и объединения их контекстов проблему двух SELECT не решить. Тут уже вам выбирать, что вам дороже - еще один roundtrip к БД или система шин. Чем, кстати, обосновано их применение в проекте? Шина, это, в общем-то EAi (Enterprise Application Integration) паттерн, вашей системе это действительно требуется и приносит пользу? Если начинаются такие вопросы, это индикатор того, что шины в проекте могут быть не к месту.
Да, часть данных(денормализованных) через шину событий реплецируется в другую базу, и часть данных берется из нее.

Использование сервиса вряд ли поможет, только если возвращать и принимать сущность, а не DTO.

Faeron
Сообщения: 9
Зарегистрирован: 2017.10.14, 19:39

Re: CRUD в DDD

Сообщение Faeron » 2017.10.28, 13:07

Nex-Otaku писал(а):
2017.10.28, 09:59
zelenin писал(а):
2017.10.27, 19:29
Nex-Otaku писал(а):
2017.10.27, 19:27
Ну и грузи их все из $entity, где тут необходимость что-то ещё селектить?
из $entity (которая dto - еще раз) в $entity (которая уже $entity)

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

$commandBus->handle(new EntityUpdateCommand($dto)); // вот тут из $dto мы грузим в $entity, которую нужно достать.
Ну а зачем её "доставать" и селектить из базы, если все её данные уже есть в DTO? Просто берёшь и создаёшь entity по полям DTO, и всё - к базе обращаться не нужно.
Частичное обновление существующей сущности.

anton_z
Сообщения: 354
Зарегистрирован: 2017.01.15, 15:01

Re: CRUD в DDD

Сообщение anton_z » 2017.10.28, 13:12

У вас в приимере шина команд и запросов, шина событий может существовать независимо от них.

Faeron
Сообщения: 9
Зарегистрирован: 2017.10.14, 19:39

Re: CRUD в DDD

Сообщение Faeron » 2017.10.28, 14:20

anton_z писал(а):
2017.10.28, 13:12
У вас в приимере шина команд и запросов, шина событий может существовать независимо от них.
Она отдельно, вызывается в обработчике команды.

anton_z
Сообщения: 354
Зарегистрирован: 2017.01.15, 15:01

Re: CRUD в DDD

Сообщение anton_z » 2017.10.29, 03:55

Faeron писал(а):
2017.10.28, 14:20

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

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

Re: CRUD в DDD

Сообщение zelenin » 2017.10.30, 13:51

anton_z писал(а):
2017.10.28, 04:12
Два SELECT тут неизбежны - так как используются шины, которые фактически разделили контексты
на самом деле разделение контекстов - формальное, т.к. фактически они могут иметь единый контекст хранилища (например при использовании доктрины identity map будет из коробки "кэшировать" запросы, т.е. он будет только один).
Так же шина/шины сами по себе может иметь единый identity map, или го-подобный контекст, итд итп.

Ответить