Как стать автором
Обновить
28
0
Кирилл @genkovich

Development Team Lead (PHP)

Отправить сообщение

У меня есть решение и кстати оно описано в комментах где-то выше. Но мне интересно посмотреть ваше предложение и узнать в чем его преимущество.

Интересно, никогда не сталкивался на практике с NAN и с INF. Спасибо.

Цель статьи, конечно не в этом, но замечание интересное. Как правите ошибку то? :)

Данное видео я привел в пример просто чтобы мы засинхронились и говорили на одном языке.

Зависимость, скрытая в static-вызове, это все равно зависимость. 

Не всё что в вендоре является кодом инфраструктуры. Вот отличная статья Нобака на эту тему.

Какая разница, вызываете вы new Entity() или Entity::newEntity()

Разница в том, что код маппинга находится в именованном конструкторе, а валидация в основном. У каждого метода есть своя причина для изменений и одна ответсвенность.

Код в статической функции все равно находится вне жизненного цикла сущности. Если вы там будете делать проверки и бросать исключения, соответствующие конкретно этому методу, это то же самое, что и делать их в вызывающем коде, только без дополнительной обертки try-catch.

Верно, я не предлагал там бросать исключения, я по прежнему предлагаю бросать их внутри __construct(), что является уже жизненным циклом сущности.

То, что бизнес-логика все равно находится не в сущности, которая сохраняется в базу.

Так, кажется я начал понимать в чем дело. Фамилия Бугаенко вам о чём-то говорит?)

Только Command уже провалидирована, и мы можем на это полагаться, а для метода сущности надо раскладывать ее на отдельные параметры, и потом внутри делать те же проверки.

А вы можете гарантировать, что command валидна? Она не содержит проверок в конструкторе. Это просто структура данных. Другой разработчик может легко создать такую команду в другом месте и впихнуть её вам забыв провалидировать.

Да, проверки для данных пользователя это одна задача. Там задача помочь пользователю правильно ввести данные. Помочь пользователю. Помочь. А проверки внутри бизнес логики другая задача - защищать свои бизнес правила. Не смотря на то, что они будут пересекаться - это разная валидация, на разных уровнях и решает разные задачи.

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

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

class SomeOrder
{
    //..
    public function changeSomeField(Author $author, Field $field)
    {
        if (! $author->isModerator()) {
            // exception
        }
        
        $this->field = $field;
    }
}

проверка, что он существует в базе будет находиться вне сущности.

Верно, будет. Вам же как правило надо не просто проверить связь. Вам надо её получить и в конечном итоге связать.

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

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

При чем тут понятие "Оверинжениринг" к "не прижилось"? Если не можете сами объяснить в чем минус - скиньте статейку или книгу, но пока я не увидел ни одного примера или строчки кода =(

что добавляемый клиент/точка находится в сфере обслуживания вашей компании, для чего используется Google maps api.

"Добавляемый", то есть есть какой-то менеджер, который выбирает координаты, которые нам нужно проверить, верно? Значит он отправляет нам Request. То есть тут мы валидируем пользовательские данные, и используем внешний апи, чтобы определить например входит ли адрес в зону доставки. Сущности как таковой еще нет, это пользовательская валидация.

Или генератор геттеров.

Звучит как что-то зловещее :)) я не являюсь категоричным в этом вопросе, что геттеров нужно избегать совсем, но правда в том что часто их лепят везде где ни попадя, тем самым раскрывая внутреннее состояние объекта, предоставляя возможность другому коду решать за него (см. пример с cancel())

пихать в VO все что по хорошему там не нужно, доступ к API например

Ни в коем случае. Эти вещи находятся на разных уровнях. Мне кажется вы путаете валидацию и правила бизнеса. Давайте на вашем примере, если я его правильно понял.

Пользователь оформляет заказ, например на доставку пиццы. Вводит свои данные, выбирает точку на карте. Бизнес вам говорит, что вы можете доставлять пиццу в радиусе определенной зоны, вполне нормальное требование.

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

Нам приходит широта и долгота. VO проверит, что точки заданного формата, что пришли обе, что вам не подсунули туда вообще что-нибудь левое. И заказ вполне может быть создан, ведь он прошел фронтовую валидацию и данные вам пришли. Значит на них надо как-то реагировать. Как варианты:

  1. Проверить на беке удаленность точки (не в VO) и сразу выдать ошибку, не создавая сущность.

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

другой — нет, потому что у первого премиум аккаунт

Отличный пример, который будет разруливаться полиморфизмом, не вижу тут проблемы. Опять же, приведите пожалуйста код и дайте больше конкретики, а то всё очень абстрактно.

И да и нет, всё таки всё зависит от задачи.

Стейт действительно хорош когда есть несколько сценариев, и например в зависимости от текущего статуса у нас будет построен разный процесс. Именно процесс. А вот за согласованность своих данных хорошо когда сущность отвечает сама. Одно другому не противоречит.

появляется термин "создание заказа", это ее реализация относится к инфраструктуре и находится в другом сервисе.

Находится он не в инфрастурктуре, а в application layer, это же бизнесовое действие. Да это юзкейс/интеррактор/комманд хендлер, не принципиально. На вход получает команду (например DTO, который составлен из пользовательского реквеста), и дальше передает его на создание, сохранение, отправку почты. Вот пример видоса, где можно посмотреть реализацию на похожем примере.

Тут вот как получается. Сущность сама себя создавать не может. Значит есть другой код, который это делает

Не важно, поднимаем мы сущность из команды или из стейта, она прекрасно с этим может справиться с помощью именованного конструктора. Смотрите паттерн репозиторий и fromState сущности Post

После создания нужно сделать дополнительные действия, связанные с данным бизнес-действием

А что вас смущает? всё находится в рамках одного юзкейса, одного сценария.

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

Приведите пример с кодом пожалуйста, я не совсем уловил мысль, как это получается :(

Дальше по комментарию я к сожалению не уловил мысль, но мне кажется мы говорим о разных подходах, которые уже никак не относятся к валидации.

Если речь о построении бизнес процесса, то выше я как раз привёл в пример видео, чтобы было понятно о чем речь.

Почему из сущности?

Сущность сможет вам гарантировать, что был указан корректный email (с точки зрения ввода), присвоить заказу номер в системе, а отправкой будет заниматься другой сервис, который уже будет использовать заведомо проверенные данные если мы ему передадим валидный order.

Нет нет, Entity тут упоминается в контексте DDD. Ни о какой доктрине нет речи.

Про автоинкремент ответил выше, ничего не ломается и можно без "кроме" ;)

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

В чем оверинжениринг, если правила валидации будут одни и те же, что в случае сервиса, что в случае валидации в сущности.

Когда у вас есть сервис валидации отдельно, а сущность отдельно, то есть возможность создать сущность в обход валидации, просто не вызвав сервис (не знали, забыли и т.д.)

Я не говорю, что геттеры сложно создать, нет, но если вы их генерируете только для валидации, то вам не кажется, что что-то вы делаете не так?

Насчёт VO - они используются внутри сущности и не надо будет делать изменения в 2х местах, достаточно только в VO. В случае с сервисом - вам придется затронуть и сущность и сервис.

Приведите пример пожалуйста, когда в одной сущности у вас 2 набора правил валидации. Не для Request объекта, а для сущности.

Перестаньте думать о базе данных. Речь идёт именно о проектировании структуры и самой сущности. Конечно, есть много случаев, когда нам нужно считаться с нашей базой (высокая конкуренция запросов, deadlocks и т.д.), но идея в том, чтобы перестроить мышление и думать об объектах, как о вещах в реальном мире, а не как о данных в таблице. Маппинг данных в это отдельная задача.

Entity нам нужны для бизнес операций, то есть когда в нашей системе происходят какие-то изменения. Если нам нужно просто вывести какие-то данные - сущности скорее всего вам не нужны, тут читать в сторону Read Model.

Полноту данных в вашей сущности определяет не таблица в БД, а условия бизнеса. Если у вас в JSON хранится какая-то информация, которую надо проверить перед изменением Entity - придётся её достать. Тут вы уже должны сами проанализировать насколько правильно спроектирована БД под ваши бизнес операции, а не наоборот.

Действительно лучше всего не накладывать ограничения, которые вам диктует база данных. Лично я давно использую генерируемые ID, которые можно присвоить сущности в любой момент и не зависеть от операции вставки.

Что касается auto increment, для этого тоже придумали механизм, в postgresql он называется sequences, копать туда.

Ну и не могу не привести короткий пост из канала :)

Могу я вас попросить привести пример кодом? Потому как даже если сущность immutable, то всё равно она создаётся через конструктор и она может и должна сама себя валидировать.

Если вы говорите об анемичной модели или классах типа DTO, то там действительно нет валидации внутри сущности, но это больше относится к инфраструктурному слою и пользовательской валидации. Об этом планирую отдельную статейку чуть позже сюда выложить.

Действительно, Order может создаться из разных endpoint, сейчас я ничего не описывал про валидацию пользователя, будет отдельная тема т.к. она общирная.

Что касается самого Order, то в вашем бизнесе это ведь что-то конкретное, например не важно откуда придёт заявка, она должна обладать email, телефоном и именем, или только email. Тут уже условия диктует как раз ваш бизнес. А вам нужно правильно перенести это на код.

Как быть с историческими данными? 

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

К последним двум абзацам отлично подойдет вот это видео. Там есть ответы на все ваши вопросы.

Спасибо за фидбек =)

Насчет интерфейса лучше документации никто не объяснит.

Интерфейс Stringable обозначает класс, реализующий метод __toString(). В отличие от большинства интерфейсов, Stringable неявно присутствует в любом классе, в котором определён магический метод __toString(), хотя он может и должен быть объявлен явно.

Другими словами - документация рекомендует объявлять явно, но если вы этого не сделаете, то всё продолжит работать.

Да, верно, как таковой проблемы в примере нет. А вот насчет синтетических или экзотических случаев я тут не совсем соглашусь. Из моей практики очень часто всплывало то, что "маловероятно" или "такого вообще никогда не произойдет". Особенно эта боль бывает когда обсуждают бизнес требования :) Но ладно, речь о другом. Так вот, введя правило для своей команды из моего предыдущего комментария, как мне кажется, мы избавляем себя от лишней головной боли и можем не думать о том "может ли здесь произойти утечка".

В таком случае она никогда не произойдет, а все места где может — они сделаны осознанно и их легче проконтролировать.

Но здесь каждый разработчик и команда должны сами выбирать, как с этим работать, главное знать, что такая ситуация возможна :)

Спасибо за фидбек.

Этот коллбек вырван из контекста. Он как раз находился в методе класса ExampleTest , который показан на скрине чуть ниже по тексту (там где я сделал дамп и показал, что внутри анонимной функции мы действительно можем обратиться к this.

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

С другой стороны, если ты просто пишешь скрипт, то static никак не повредит. В целом можно в команде договориться о правиле вроде "Ставь везде static, убирай только там, где обращаешься к $this" (по аналогии с final напр.).

Да, читал и периодически возвращаюсь, каждый раз обращая внимание на новые детали :)

Я имел ввиду по теме LSP

Интересное замечание, я даже залез в оригинальный доклад самой Лисков. Не уверен, что всё так однозначно. В оригинальной статье везде используются понятия "subtype" и "supertype", а также "inherited methods", где уточняется, что "We do not mean that the subtype inherits the code of these methods but simply that it provides methods with the same behavior as the corresponding supertype methods."

Да и в статье, что вы привели Мартин пишет "People (including me) have made the mistake that this is about inheritance. It is not. It is about sub-typing. All implementations of interfaces are subtypes of an interface." То есть он не сказал "It is about implementations of interfaces", возможно как раз потому, что не только к наследованию (которое также заставляет вас использовать интерфейс супертипа), но и к имплементации интерфейсов это применимо.

Но круто, если есть еще интересные источники - кидайте, интересно почитать.

Так разве SOLID заставляет вас искать абстракцию у кошки и лимона?

Мне кажется в данном контексте вы путаете с DRY, но там тоже есть свои оговорки, что его надо использовать с умом.

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

Если у вас есть источники с альтернативной информацией - поделитесь, очень интересно другое мнение.

1

Информация

В рейтинге
Не участвует
Откуда
Одесса, Одесская обл., Украина
Дата рождения
Зарегистрирован
Активность