Грамотное ООП: Видео доклада с PHP Russia 2019

После нашего отчёта о конференции по разрешению координатора Егора публикую запись своего доклада на PHP Russia. Покапитанил про важность разделения кода и инкапсуляции бизнес-логики в сервисах и сущностях:

Доклад построен на основе материалов мастер-класса по Symfony с Doctrine. Также более подробные примеры проектирования есть в эпизодах о конструкторах и методах нашей базы знаний.

Везде меня веселее смотреть на скорости 1,25. И обязательно подписаться на мою рассылку:

Комментарии

 

Дмитрий

"Слайды на 100 страниц. Извините))" - классика))

Ответить

 

Иван

Дмитрий молодец!
Классное видео, очень просто и полезно!

Ответить

 

Yurii

Здравствуйте, Дмитрий!

Очень увлекательный доклад, спасибо.
Есть один вопрос:

Что делать, если бизнес-логика зависит от юз-кейса?

Приведу синтетический пример:

class Post
{
    public function publish()
    {
        if($this->status === self::IN_MODERATION)
        {
             throw new \DomainException('Not allowed');
        }
    }
}

Но, к примеру, если это пост публикует админ, то это условие не нужно применять.

Бизнес-логика должна лежать в слое домена, но что делать, если в каком-то юз-кейсе бизнес-правила отличаются или даже противоречат общим? Писать бизнес-логику в слое приложения в юз-кейсах ведь не правильно.

Но и написать 2 метода в модели для этих целей не выглядит правильным решением, потому что тогда есть способ обойти инварианты в клиентском коде.

Как Вы считаете? Спасибо

Ответить

 

Дмитрий Елисеев

Делаю publishByAuthor() и publishByAdmin().

Ответить

 

Yurii

Не будем смотреть на тесты.

Вы можете вызвать publishByAdmin там, где это не стоит делать и с точки зрения DDD инвариант будет нарушен, потому что метод модели семантически зависит от юз-кейса

Ответить

 

Дмитрий Елисеев

С точки зрения сущности всё правильно. Это юзкейс должен решить, админ это или нет.

Ответить

 

Artem

Дмитрий, спасибо за то, что вы делаете!

Ответить

 

Artem

Спасибо за доклад, Дмитрий!

А бывает случай, когда в микросервисной архитектуре бизнес-логика размазана по сервисам.
К примеру, удалить сущность можно только тогда, когда другой сервис дает добро.

Например, удалить пользователя (сервиса пользователей), у которого нет лотов (http кол в лот сервис).

Куда в этом случае ложить эти проверки и на каком уровне защищать это бизнес правило, если оно неделимо?

Ответить

 

Дмитрий Елисеев

В микросервисах это либо дёргается через HTTP из Gateway, либо работает на событиях и управляется сагами.

Ответить

 

xfg

Дмитрий, есть ли в планах провести мастер-класс или написать статью по сагам? Интересует saga orchestration.

Ответить

 

Дмитрий Елисеев

Да, можно что-нибудь такое придумать.

Ответить

 

Леонид Черненко

Очень крутой доклад. Спасибо.

Есть ли существенные минусы у Доктрины?

Есть ли сейчас достойная замена Доктрине? Какая-либо другая библиотека, позволяющая писать код по DDD. Чтобы уровень удобства был не меньше чем у Доктрины.

Ответить

 

Дмитрий Елисеев

Из неудобств - требование использовать её ArrayCollection для связей и необходимость передачи объекта для связей.

По её аналогии, но с экономией памяти для асинхронных приложениях пишут Cycle ORM.

А если Doctrine недостаточно (что редкость), то уже пишут свои репозитории и гидраторы.

Ответить

 

Леонид Черненко

Спасибо )

Ответить

 

Александр

1. В чем профит создания связи с помощью Member/Id, почему не передаете непосредственно объект Member в конструктор лота? Ну и соответственно memberId - строка, вместо ManyToOne?
2. Если работаем со ставками через лот, то как быть, если, к примеру, надо вывести постранично список всех ставок? Работать с массивом объектов может быть накладно

Ответить

 

Дмитрий Елисеев

1. Без Doctrine (которой для связи нужна целая сущность) в сущность нет смысла создавать и передавать целого мембера, если от него нужен только ID. Особенно в тестах.

2. Выполняем напрямую SELECT FROM bids INNER JOIN lots и выводим список. Тяжёлые доменные сущности в листингах выводить нецелесообразно и по производительности.

Ответить

 

Александр

1. А с доктриной? Все-таки в примере используется она
2. Т.е. для вывода данных доменные сущности не используются? В таком случае в каком слое будет находится код, реализующий эту функциональность?

Ответить

 

Дмитрий Елисеев

1. С Doctrine для связей передаётся весь Member.

2. Верно. Доменные сущности только для домена. А для вывода делают отдельные ReadModel.

Ответить

 

Егор

Под UseCase (если прямо по тексту то что-то типа UseCase\SignUp\Request) подразумевается namespace для объединения Command и Handler или я где-то пропустил все-таки отдельный класс под UseCase? В коде презентации в контроллерах же просто везде хендлер дергается с передаваемой ему командой!

Ответить

 

Дмитрий Елисеев

Именно операция. А одним классом-сервисом или пачкой классов в папке в папке - не важно.

Ответить

 

Егор

А если вот у меня например use case можно обозвать как GetRate, "Получить цену". То есть я хочу в API, web-морду, CLI или еще как-то вернуть некую цену на мой товар. Наверное, тогда стоит именовать классы GetRateQuery и GetRateQueryHandler соответственно. И у GetRateQueryHandler должен быть, наверное, не handle(): void, а что-то типа getResult():mixed ?

Ответить

 

Дмитрий Елисеев

Да, запросы можно как раз именовать как Query.

Ответить

 

Егор

Да у меня тут больше вопрос был про handle():void, если для Command, и hande():mixed для Query. То есть нужно ли разделять хендлеры (по их интерфейсам имею ввиду) для Command и для Query, ибо, судя по всему, в ответ на запрос все-таки нужно что-то более существенное чем void вернуть :).

Ответить

 

Дмитрий Елисеев

Да, можно для запросов сделать папку вроде Query или ReadModel как здесь.

Ответить

 

Егор

Спасибо!

Ответить

Оставить комментарий

Войти | Завести аккаунт | Войти через

(никто не увидит)



Можно использовать теги <p> <ul> <li> <b> <i> <a> <pre>