CRUD в DDD
CRUD в DDD
Доброго времени суток.
Подскажите, пожалуйста, как быть с 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'ов?
Какие еще есть варианты?
Спасибо!
Подскажите, пожалуйста, как быть с 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
- Сообщения: 3134
- Зарегистрирован: 2009.04.02, 21:42
- Откуда: Valencia
- Контактная информация:
Re: CRUD в DDD
> Если так, то чем это отличается от setter'ов?
Сеттерами называют методы которые открывают доступ к смене значения инкапсулированного свойства объекта, без никакой дополнительной логики.
Смена `username` и `password` это change credentials, уж точно такая смена влечет кучу бизнес процессов нужно проверить.
Смена `status` (по названию) я предлагаю что это тоже смена состояния сущности и тоже куча инвариантов при этом надо удовлетворить.
Теоретически, такой метод не является сеттером, т.к. инкапсулирует два инварианта
Если всего этого нет, и тут простой CRUD нужен загрузил данные из БД, сменил свойства и пересохранил, то используйте CRUD.
Не верьте никому кто говорит что CRUD плохой, DDD рулит. Требования к приложению диктуют что юзать а не программисты и их фанатизм.
Сеттерами называют методы которые открывают доступ к смене значения инкапсулированного свойства объекта, без никакой дополнительной логики.
Смена `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 плохой, DDD рулит. Требования к приложению диктуют что юзать а не программисты и их фанатизм.
Жду Yii 3!
Re: CRUD в DDD
Спасибо за ответ.
По мимо CRUD, присутствует и бизнес-логика, по этому и вопрос как это совместить.
По мимо CRUD, присутствует и бизнес-логика, по этому и вопрос как это совместить.
Re: CRUD в DDD
Прошу помочь,
В административном интерфейсе, есть некая сущность, которую можно создавать, удалять, редактировать (CRUD).
При этом интерфейс просмотра сущности, совмещен с редактированием, т.е. большую часть атрибутов сущности маппится на форму для редактирования.
DTO
ActiveFrom в шаблоне
Controller
Но при таком подходе получается, что при отправке формы будет 2 select'а к БД, сначала получаем DTO в контроллере, потом сущность в обработчике команды.
Есть вариант разнести экшены в контроллере, один будет отрисовывать форму, второй, обрабатывать.
правильно ли я понимаю, как организовать подход?
В административном интерфейсе, есть некая сущность, которую можно создавать, удалять, редактировать (CRUD).
При этом интерфейс просмотра сущности, совмещен с редактированием, т.е. большую часть атрибутов сущности маппится на форму для редактирования.
DTO
Код: Выделить всё
<?php
class Entity extends \yii\base\Model
{
public $name;
public $someFiled;
...
public function rules()
{
return [
// валидация
];
}
}
Код: Выделить всё
<?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(); ?>
Код: Выделить всё
?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]);
}
}
Есть вариант разнести экшены в контроллере, один будет отрисовывать форму, второй, обрабатывать.
правильно ли я понимаю, как организовать подход?
Re: CRUD в DDD
ну и? а) 2 разных сервиса делают 2 разных запроса - это нормально б) вопрос вашей реализации БД-слоя - можно кэшировать загруженные сущности в памяти (см. Identity Map)
при любой непонятной фигне разноси экшны.
Re: CRUD в DDD
Не вижу здесь двух селектов.
Данные из БД извлекаются только здесь
Дальше эти данные передаются в другие команды.
Данные из БД извлекаются только здесь
Код: Выделить всё
$entity = $queryBus->handle(new GetEntityById($id));
Re: CRUD в DDD
Правильно, но команда это тоже DTO, по этому в обработчике команды, опять происходит select сущности (id берется из команды), которая обновляется данными из командыNex-Otaku писал(а): ↑2017.10.27, 12:19 Не вижу здесь двух селектов.
Данные из БД извлекаются только здесьДальше эти данные передаются в другие команды.Код: Выделить всё
$entity = $queryBus->handle(new GetEntityById($id));
Re: CRUD в DDD
тогда называйте $dto, а не $entity. И вообще dto - юзкейс-специфичная вещь, то есть к определенному экшну относится, и получать ее нужно в экшне, а не в шине.Faeron писал(а): ↑2017.10.27, 12:29Правильно, но команда это тоже DTO, по этому в обработчике команды, опять происходит select сущности (id берется из команды), которая обновляется данными из командыNex-Otaku писал(а): ↑2017.10.27, 12:19 Не вижу здесь двух селектов.
Данные из БД извлекаются только здесьДальше эти данные передаются в другие команды.Код: Выделить всё
$entity = $queryBus->handle(new GetEntityById($id));
Re: CRUD в DDD
Вот здесь селект второй раз вызывается?
Код: Выделить всё
EntityUpdateCommand($entity)
Re: CRUD в DDD
потому что нам данные из dto (помним что $entity на самом деле dto?) надо загрузить в сущность, а потом сущность сохранить.Nex-Otaku писал(а): ↑2017.10.27, 19:11Вот здесь селект второй раз вызывается?Но зачем? Данные-то уже есть в $entity, зачем их повторно извлекать? В чём смысл? Зачем читать что-то из БД, когда всё уже есть в $entity?Код: Выделить всё
EntityUpdateCommand($entity)
Re: CRUD в DDD
Re: CRUD в DDD
из $entity (которая dto - еще раз) в $entity (которая уже $entity)
Код: Выделить всё
$commandBus->handle(new EntityUpdateCommand($dto)); // вот тут из $dto мы грузим в $entity, которую нужно достать.
Re: CRUD в DDD
Два SELECT тут неизбежны - так как используются шины, которые фактически разделили контексты. В приведенном примере в одном контексте данные читаются, в другом пишутся (при записи нужно сначала проверить, есть ли такой объект в хранилище, поэтому требуется второй SELECT). Есть еще контекст контроллера - там у вас только dto. Без демонтажа шин и объединения их контекстов проблему двух SELECT не решить. Тут уже вам выбирать, что вам дороже - еще один roundtrip к БД или система шин. Чем, кстати, обосновано их применение в проекте? Шина, это, в общем-то EAi (Enterprise Application Integration) паттерн, вашей системе это действительно требуется и приносит пользу? Если начинаются такие вопросы, это индикатор того, что шины в проекте могут быть не к месту.
Re: CRUD в DDD
Ну а зачем её "доставать" и селектить из базы, если все её данные уже есть в DTO? Просто берёшь и создаёшь entity по полям DTO, и всё - к базе обращаться не нужно.zelenin писал(а): ↑2017.10.27, 19:29из $entity (которая dto - еще раз) в $entity (которая уже $entity)Код: Выделить всё
$commandBus->handle(new EntityUpdateCommand($dto)); // вот тут из $dto мы грузим в $entity, которую нужно достать.
Re: CRUD в DDD
Да, часть данных(денормализованных) через шину событий реплецируется в другую базу, и часть данных берется из нее.anton_z писал(а): ↑2017.10.28, 04:12 Два SELECT тут неизбежны - так как используются шины, которые фактически разделили контексты. В приведенном примере в одном контексте данные читаются, в другом пишутся (при записи нужно сначала проверить, есть ли такой объект в хранилище, поэтому требуется второй SELECT). Есть еще контекст контроллера - там у вас только dto. Без демонтажа шин и объединения их контекстов проблему двух SELECT не решить. Тут уже вам выбирать, что вам дороже - еще один roundtrip к БД или система шин. Чем, кстати, обосновано их применение в проекте? Шина, это, в общем-то EAi (Enterprise Application Integration) паттерн, вашей системе это действительно требуется и приносит пользу? Если начинаются такие вопросы, это индикатор того, что шины в проекте могут быть не к месту.
Использование сервиса вряд ли поможет, только если возвращать и принимать сущность, а не DTO.
Re: CRUD в DDD
Частичное обновление существующей сущности.Nex-Otaku писал(а): ↑2017.10.28, 09:59Ну а зачем её "доставать" и селектить из базы, если все её данные уже есть в DTO? Просто берёшь и создаёшь entity по полям DTO, и всё - к базе обращаться не нужно.zelenin писал(а): ↑2017.10.27, 19:29из $entity (которая dto - еще раз) в $entity (которая уже $entity)Код: Выделить всё
$commandBus->handle(new EntityUpdateCommand($dto)); // вот тут из $dto мы грузим в $entity, которую нужно достать.
Re: CRUD в DDD
У вас в приимере шина команд и запросов, шина событий может существовать независимо от них.
Re: CRUD в DDD
на самом деле разделение контекстов - формальное, т.к. фактически они могут иметь единый контекст хранилища (например при использовании доктрины identity map будет из коробки "кэшировать" запросы, т.е. он будет только один).
Так же шина/шины сами по себе может иметь единый identity map, или го-подобный контекст, итд итп.