Страница 2 из 6

Re: Проектирование сущностей, сервисов и репозиториев

Добавлено: 2017.04.07, 14:19
zelenin

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

 public function rename(EmployeeId $id, NameDto $dto)
    {
        $employee = $this->employees->get($id);
        $employee->rename(new Name(
            $dto->last,
            $dto->first,
            $dto->middle
        ));
        $this->employees->save($employee);
        $this->dispatcher->dispatch($employee->releaseEvents());
    }
события все же кидать в диспетчер лучше непосредственно в репозитории, в методах где происходят изменения - так достигнем атомарности "сохранение=>события".

Re: Проектирование сущностей, сервисов и репозиториев

Добавлено: 2017.04.07, 15:17
ElisDN
zelenin писал(а): 2017.04.07, 14:19 события все же кидать в диспетчер лучше непосредственно в репозитории, в методах где происходят изменения - так достигнем атомарности "сохранение=>события".
Да, но если вызываем сохранение у несколько репозиториев (обернув в TransactionManager или TransactionalCommandBus) то это не прокатит.

Re: Проектирование сущностей, сервисов и репозиториев

Добавлено: 2017.04.07, 15:27
samdark
Написал мысли постом: http://rmcreative.ru/blog/post/servisny ... ery--mysli

Re: Проектирование сущностей, сервисов и репозиториев

Добавлено: 2017.04.07, 15:28
zelenin
ElisDN писал(а): 2017.04.07, 15:17
zelenin писал(а): 2017.04.07, 14:19 события все же кидать в диспетчер лучше непосредственно в репозитории, в методах где происходят изменения - так достигнем атомарности "сохранение=>события".
Да, но если вызываем сохранение у несколько репозиториев (обернув в TransactionManager или TransactionalCommandBus) то это не прокатит.
почему? каждый репозиторий кинет свои события, сохранив свои сущности. (про атомарность я в контексте одного места сохранения/релиза событий, а не про транзакции).

Re: Проектирование сущностей, сервисов и репозиториев

Добавлено: 2017.04.07, 15:35
ElisDN
zelenin писал(а): 2017.04.07, 15:28 почему? каждый репозиторий кинет свои события, сохранив свои сущности.
Первые два кинут, а третий вылетит с RuntimeException и всё откатит:

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

$this->transactionManager->execute(function () use ($interview, $employee, $contract) {
    $this->employeeRepository->add($employee);
    $this->contractRepository->add($contract);
    $this->interviewRepository->save($interview);
});
а события из первых двух уже ушли...

Re: Проектирование сущностей, сервисов и репозиториев

Добавлено: 2017.04.07, 15:48
zelenin
это собственно обычная проблема при смешивании транзакций с нетранзакционными вещами. В данном случае проблема в слишком синхронном диспетчере. Решить проблему можно а) накоплением пула эвентов и выполнением их после выполнения основной транзакции, б) персистом эвентов, в) асинхронной шиной.
Тут же получается протечка знания о реализации одного слоя в реализацию другого слоя.

Re: Проектирование сущностей, сервисов и репозиториев

Добавлено: 2017.04.07, 17:31
ElisDN
zelenin писал(а): 2017.04.07, 15:48 это собственно обычная проблема при смешивании транзакций с нетранзакционными вещами.
С TransactionalCommandBus это решается подсовыванием DefferedEventDispatcher с накопительным методом dispatch(), чтобы после успешного выполнения хэндлера в транзакции запускать обработку вызовом $dispatcher->relaseAll(). А при работе без шины (напрямую с сервисом) это реализовать уже проблематично.

Re: Проектирование сущностей, сервисов и репозиториев

Добавлено: 2017.04.08, 13:28
ElisDN
Добавил третью часть с реализацией репозитория.

Re: Проектирование сущностей, сервисов и репозиториев

Добавлено: 2017.04.08, 14:53
glagola
Я DDD увлекся иключительно благодаря данному форуму! А теперь есть еще и детальные статьи раскрывающие тему, спасибо вам!

Вот из моей небольшой практики, относительно методов add и save из вашей статьи:

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

interface EmployeeRepository
{
    ....
    /**
     * @param Employee $employee
     */
    public function add(Employee $employee);
 
    /**
     * @param Employee $employee
     */
    public function save(Employee $employee);
    ....
}
У меня один метод "save" в репозиториях, а понять insert/update можно двумя способами:
  • Если у вас в IdentityMap хранятся все загруженные сущности в рамках текущего запроса, то, при вызове метода save, можно проверить есть ли в IdentityMap сохраняемый объект: есть (update), нету (insert)
  • Сохранять все сгенерированные nextId() в отдельный массив в репозитории, чтобы при сохранении реализовать логику аналогичную первому варанту.

Re: Проектирование сущностей, сервисов и репозиториев

Добавлено: 2017.04.08, 15:56
ElisDN
glagola писал(а): 2017.04.08, 14:53 Если у вас в IdentityMap хранятся все загруженные сущности
Да, но только если есть IdentityMap.

Re: Проектирование сущностей, сервисов и репозиториев

Добавлено: 2017.04.11, 19:14
glagola
Цитата из статьи про репозитории :
Контейнер Yii написан так, что Instance::of может принимать не только имена элементов контейнера, но и сервис-локатора Yii::$app, поэтому мы можем указать напрямую Instance::of('db') и он поймёт, что мы от него хотим именно объект Yii::$app->db
Я, когда это увидел, обрадовался, что больше не нужно лепить лямбды, чтобы заинжектить \Yii::$app->db, но когда попробовал в тестовом экшене консольного контроллера, задекларировать, а потом и создать:

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

\Yii::$container->setSingleton(
    UserMembershipDetailsRepository::class,
    UserMembershipDetailsRepository::class,
    [Instance::of('db')]
);
$repository = \Yii::createObject(UserMembershipDetailsRepository::class);
Получил ошибку
Exception 'ReflectionException' with message 'Class db does not exist'
Вы точно, нигде не задекларировали db в DI-конейнере? как-то так:

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

\Yii::$container->setSingleton('db', function () {
    return \Yii::$app->db;
});
Потому как если он заранее задекларирован, то все работает.

P.S. версия Yii 2.0.11.2

Re: Проектирование сущностей, сервисов и репозиториев

Добавлено: 2017.04.11, 20:03
ElisDN
glagola писал(а): 2017.04.11, 19:14 Получил ошибку 'Class db does not exist'
А в консольной конфигурации компонент 'db' настроен?

Re: Проектирование сущностей, сервисов и репозиториев

Добавлено: 2017.04.11, 21:39
glagola
А в консольной конфигурации компонент 'db' настроен?
Не думаю что это имеет отношение к делу (другое дело если бы контейнер вернул не инициализированный инстанс Connect), но, в любом случае, если вы имеете ввиду, настроено ли подключение к БД - да у него рабочая конфигурация.

Вот так работает:

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

\Yii::$container->setSingleton('db', function () {
    return \Yii::$app->db;
});
\Yii::$container->setSingleton(
    UserMembershipDetailsRepository::class,
    UserMembershipDetailsRepository::class,
    [Instance::of('db')]
);
$repository = \Yii::createObject(UserMembershipDetailsRepository::class);
А вот так нет:

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

\Yii::$container->setSingleton(
    UserMembershipDetailsRepository::class,
    UserMembershipDetailsRepository::class,
    [Instance::of('db')]
);
$repository = \Yii::createObject(UserMembershipDetailsRepository::class);
В комментариях к классу yii\di\Instance есть пример, который, собственно, и натолкнул меня на мысль, что вы еще где-то прописали db.

P.S. есть еще одно отличие от вашего демо-github-репозитория - у меня Advanced application template, хотя я не думаю, что такие core фичи могут различаться.

Re: Проектирование сущностей, сервисов и репозиториев

Добавлено: 2017.04.11, 22:15
ElisDN
glagola писал(а): 2017.04.11, 21:39 ...
Посмотрел в исходники и увидел, что из Yii::$app он дёргает только в `Instance:ensure`. Поменял у себя тоже на:

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

$container->setSingleton('db', function () use ($app) {
    return $app->db;
});

$container->setSingleton(EmployeeRepository::class, SqlEmployeeRepository::class, [
    Instance::of('db'),
]);

Re: Проектирование сущностей, сервисов и репозиториев

Добавлено: 2017.04.17, 13:16
pistol
А если у сущности Employee 30 полей и каждый месяц добавляется новое из того, что "забыли" при проектировании? Через конструтор все передавать и каждый раз не забывать во всех других сервисных классах добавлять передачу в конструтор? Или есть какая-то хорошая практика для таких сложных сущностей (кроме передачи "черного" массива в конце))) ?

Re: Проектирование сущностей, сервисов и репозиториев

Добавлено: 2017.04.17, 17:24
slavcodev
Есть хорошие практики, любой патерн из категории Creational patterns

Re: Проектирование сущностей, сервисов и репозиториев

Добавлено: 2017.04.17, 17:52
pistol
Спасибо. Попробую попрактиковаться на боевом примере.

Re: Проектирование сущностей, сервисов и репозиториев

Добавлено: 2017.04.21, 09:32
ElisDN
Добавил новую часть про Doctrine.

Re: Проектирование сущностей, сервисов и репозиториев

Добавлено: 2017.05.07, 11:50
ElisDN
Добавил часть про ActiveRecord.

Re: Проектирование сущностей предметной области

Добавлено: 2017.10.12, 11:51
ElisDN
sda писал(а): 2017.04.01, 06:18 Наверное имеет ввиду отсутствие транзакций в nosql. Но мне интересно, для чего Дмитрий бы их использовал в nosql, ведь Вон Вернон пишет, что сохранять нужно не более 1 агрегата внутри транзакции. Но в nosql мы можем и без транзакции атомарно сохранить весь агрегат одной целостной json структурой. Тогда для решения какой проблемы нужны транзакции в nosql ?
При оформлении заказа в магазине с подарочным сертификатом + балансом нужно:

- Создать и сохранить заказ
- Списать наличие и пересохранить в цикле все товары
- Списать с баланса и пересохранить аккаунт
- Закрыть и пересохранить сертификат
- Начислить бонусных баллов и пересохранить профиль
- Пересчитать рейтинг продавца и пересохранить профиль
- Начислить комиссию партнёру и пересохранить профиль
- ...

Это уже десятки json-структур, а не одна.