Раз уж это гайд на пакет - имеет смысл добавить пару слов и о .gitattributes. В него стоит добавить директории и файлы, которые не нужно распространять с пакетом - тесты, фыйлы конфигов, докерфайлы, ci/cd и прочее, что не нужно конечному пользователю.
Пример можно подсмотреть в почти любом распространнённом php-пакете:
В коде всё конкретно. Код либо работает, либо нет.
Либо быстро, либо медлено. Либо в соответвии с ожиданиями програмиста его написавшего, либо нет. Либо в соответвии с требованиями для реализации которых он был написан, либо нет. Либо он понятен другим разработчикам, либо становится их головной болью. Либо он сопровождаемый и расширяемый, либо превращается в технический долг. Либо он безопасен и надёжен, либо источник уязвимостей и багов. Либо его можно протестировать, либо остаётся верить, что «оно как-то работает». Либо он служит решению задачи, либо создаёт новые.
Регистрозависимость не является источником этой проблемы. Даже не играясь с регистром без проблем можно понасписывать слов, вглядящих одинаково, но составленных на самом деле из разных символов.
Вот к примеру слово 'Регистрозависимость', можно написать 32 разными способами просто подменяя кирилические буквы 'o' и 'c'.
Для тестов пришел к аналогичному решению с гибкими фабриками, которые позволяют не передавать не релевантные для тест кейса данные.
От значений по умолчанию на уровне базы постарался отказаться. Профита от них не вижу, а логика в итоге размазывается между приложением и хранилищем.
Дефолтные значения для полей что могут быть не переданы в рамках нормальных сценариев работы приложения - стараюсь определять в конструкторе сущности.
Для случаев, когда как в примере из пункта #4, часть аргументов конструктора опциональны, но при этом обязывает наличие другого опционального параметра - делаю конструктор приватным, и создаю два публичных фабричных метода один из которых требует обязательную передачу обоих связанных аргументов, а второй не предполагает их передачи вовсе. Это если эти варианты вызываются при разных сценариях. Если комбинаций куда больше, либо язык не предусматривает таких конструкция - обьединяю связанные аргументы в одну структуру, даже если это требуется всего в одном месте
Интерфейсы, если простыми словами, нужны для передачи/получения определенных данных от разных источников
А если сложными - для инверсии и управления зависомстями между компонентами приложения. В таком случае определить необходимость наличия интерфейса, по коду его реализации невозможно.
Докину еще вариант в тред обсуждения самовалидируемости сущностей и необходимости подключения репозитория для проверки уникальности email. Я для себя решил эту дилему перестав называть юзера с дублирующим email невалидным. Юзер валидный, но его сохранение приведет систему к невалидному состоянию чего мы и пытаемя избежать. Это же касается других правил которые нельзя првоерить в рамках одной сущности\агрегата.
Мы такую проблему решаем вынося всю логику касающуюся более чем одной сущности в доменные сервисы.
Но я бы делал не просто сервис проверки уникальности Email, а сразу сервис создания юзера, который бы проверял наличие дублей в репозитории еще до создания самой сущности.
<?php
class CreateUserService
{
UserRepositoryInterface repository
handle(string name, string password, string email)
{
# У нас нет никакой необходимости получать юезра из репозиятория, достаточно спросить о его существовании
if (repository.containtUserWith(email)) {
throw new BusinessException.EmailIsNotUnique();
}
usersRepository.save(new User(request.Name, request.Password, request.Email));
}
}
Что мне определенно не нравится в предложенном варианте c ивентами:
Доменный ивент отправляется до комита транзакции.
Доменный ивент содержит невалидную сущность.
К примеру захотев отправить юзеру email-нотификашку о регистрации, логично бьло бы делать это в ответ на доменное событие создания юзера, но у нас этот ивент не является свидельством создания юзера, а лишь свидетельством попытки.
Вместо того чтобы предотвраить перевод системы в невалидное состояние, мы разрешаем ей к нему придти, и только потом проверяем всё ли в порядке и в случае чего пытаемся исправить ситуацию.
DDD не запрещает сделать email уникальным на уровне базы для подстраховки, но наличие проверки в коде позволит:
Оставить описание требований к этой уникальности в доменном слое приложения.
Проведя явную проверку вернуть пользователю ошибку о нарушении конкретного правила, а не отлавливать исключения из базы и пытаться понять что же это за исключение и как обьяснять его пользователю.
Хотелось бы поблагодарить автора за авторскую статью с примерами кода. Приятно натыкатся на хабре на что-то-то помимо копирайтерского треша и корявых переводов. Добавить бы еще подстветку PHP-шного синтаксиса в во вставки кода.
С каменным лицом, дизайн который не удовлетворяет все возможные требования которые могут возникнуть к нему в будующем, называть мусором это конечно сильно.
В первом же случае мы фактически пишем не юнит-тест, а интеграционный тест на сущность, который к тому же гораздо более требователен к ресурсам при запуске.
Видимо у нас разные понятия о сущностях или тестах.
В моем понимании сущность это Data Mapper + Rich Domain Model в которую помещается вся бизнес-логика для реализации которой не труебуется взаимодействие с другими сервисами. Таким образом ни для её создания ни для вызова её методов никакие другие сервисы не нужны, и тестировать её можно простым Unit-тестом.
Отдельно тестировать бизнес-логику, не тестируя сущности зачастую гораздо проще. Хотя-бы просто потому, что тестируется только изолированная часть логики.
Для тестирования бизнес-логики размещенной в сущности необходимо:
Экземпляра сущности.
Для тестирования бизнес-логики размещенной в сервисе необходимо:
А я хоть где-то упоминал про полный отказ от мышки?
Отказался я от нее прежде всего при навигации между файлами\папками. Если подходить к именованию классов и структуре проекта системно - мне куда проще через Ctrl + Shift + N ввести название класса либо его аббревиатуру, чем перемещаться к нему через древо каталогов. И чем больше проект тем более такой подход мне кажется комфортнее.
Как раз таки на маленьких проектиках где все файлы умещаются в окне Project такой подход уже кажется избыточным. При работе с чужими исходниками, пока с ним не ознакомишься мне тоже удобнее гулять мышью. Но большая часть кода с которой приходится работать либо написана мной, либо написана в соответствии с тем же набором правил которому следую я, что тоже способствует быстрому пониманию структуры.
В котором до сих пор ломается что-то что отлично работает в старом интерфесе.
если вы ими уже пользуетесь, то переход становится в разы проще
Или сложнее. У меня регулярно перестают работать хоткеи перехода в пункты меню через Alt + {FirstLetter}, в итоге вместо отрытия меню Git нажатием Alt + G, мне приходится перебирать хоткеи других пунктов и уже после нахождения работающего переходить в Git.
Начну с того что не пытаюсь никого переубедить в том кому как удобней. Вопрос привычек, предпочтений и опыта работы с конкретной конфигурацией.
Вам это удобнее?
Да. Я никогда эти шорткаты не заучивал. Увидел -> решил что это удобней чем навигация мышкой -> начал использовать. Пол года назад обнаружил что табы вообще не использую и выключил их.
Вместо 1 клика мышкой с минимальной когнитивной нагрузкой
Вместо того чтобы тянутся к мышке, а потом обратно, я жму клавиши которые вот уже под руками. Когнитивной нагрузки это создает не больше чем нажать F для очередного foreach.
Ctrl + E открывает похожий список с возможность фильтрации по названию. Тоже отказался от табов в пользу Ctrl + N \ Ctrl + E \ Ctrl + Tab \ Ctrl + Alt + Right \ Ctrl + Alt + Left
Раз уж это гайд на пакет - имеет смысл добавить пару слов и о .gitattributes.
В него стоит добавить директории и файлы, которые не нужно распространять с пакетом - тесты, фыйлы конфигов, докерфайлы, ci/cd и прочее, что не нужно конечному пользователю.
Пример можно подсмотреть в почти любом распространнённом php-пакете:
composer/composer
phpunit/phpunit
phpmailer/phpmailer
monolog/monolog
Либо быстро, либо медлено.
Либо в соответвии с ожиданиями програмиста его написавшего, либо нет.
Либо в соответвии с требованиями для реализации которых он был написан, либо нет.
Либо он понятен другим разработчикам, либо становится их головной болью.
Либо он сопровождаемый и расширяемый, либо превращается в технический долг.
Либо он безопасен и надёжен, либо источник уязвимостей и багов.
Либо его можно протестировать, либо остаётся верить, что «оно как-то работает».
Либо он служит решению задачи, либо создаёт новые.
Как всё просто и конкретно)
Регистрозависимость не является источником этой проблемы.
Даже не играясь с регистром без проблем можно понасписывать слов, вглядящих одинаково, но составленных на самом деле из разных символов.
Вот к примеру слово 'Регистрозависимость', можно написать 32 разными способами просто подменяя кирилические буквы 'o' и 'c'.
Для тестов пришел к аналогичному решению с гибкими фабриками, которые позволяют не передавать не релевантные для тест кейса данные.
От значений по умолчанию на уровне базы постарался отказаться. Профита от них не вижу, а логика в итоге размазывается между приложением и хранилищем.
Дефолтные значения для полей что могут быть не переданы в рамках нормальных сценариев работы приложения - стараюсь определять в конструкторе сущности.
Для случаев, когда как в примере из пункта #4, часть аргументов конструктора опциональны, но при этом обязывает наличие другого опционального параметра - делаю конструктор приватным, и создаю два публичных фабричных метода один из которых требует обязательную передачу обоих связанных аргументов, а второй не предполагает их передачи вовсе. Это если эти варианты вызываются при разных сценариях. Если комбинаций куда больше, либо язык не предусматривает таких конструкция - обьединяю связанные аргументы в одну структуру, даже если это требуется всего в одном месте
А если сложными - для инверсии и управления зависомстями между компонентами приложения. В таком случае определить необходимость наличия интерфейса, по коду его реализации невозможно.
Докину еще вариант в тред обсуждения самовалидируемости сущностей и необходимости подключения репозитория для проверки уникальности email.
Я для себя решил эту дилему перестав называть юзера с дублирующим email невалидным.
Юзер валидный, но его сохранение приведет систему к невалидному состоянию чего мы и пытаемя избежать.
Это же касается других правил которые нельзя првоерить в рамках одной сущности\агрегата.
Мы такую проблему решаем вынося всю логику касающуюся более чем одной сущности в доменные сервисы.
Но я бы делал не просто сервис проверки уникальности Email, а сразу сервис создания юзера, который бы проверял наличие дублей в репозитории еще до создания самой сущности.
Что мне определенно не нравится в предложенном варианте c ивентами:
Доменный ивент отправляется до комита транзакции.
Доменный ивент содержит невалидную сущность.
К примеру захотев отправить юзеру email-нотификашку о регистрации, логично бьло бы делать это в ответ на доменное событие создания юзера, но у нас этот ивент не является свидельством создания юзера, а лишь свидетельством попытки.
Вместо того чтобы предотвраить перевод системы в невалидное состояние, мы разрешаем ей к нему придти, и только потом проверяем всё ли в порядке и в случае чего пытаемся исправить ситуацию.
DDD не запрещает сделать email уникальным на уровне базы для подстраховки, но наличие проверки в коде позволит:
Оставить описание требований к этой уникальности в доменном слое приложения.
Проведя явную проверку вернуть пользователю ошибку о нарушении конкретного правила, а не отлавливать исключения из базы и пытаться понять что же это за исключение и как обьяснять его пользователю.
Хотелось бы поблагодарить автора за авторскую статью с примерами кода.
Приятно натыкатся на хабре на что-то-то помимо копирайтерского треша и корявых переводов.
Добавить бы еще подстветку PHP-шного синтаксиса в во вставки кода.
С каменным лицом, дизайн который не удовлетворяет все возможные требования которые могут возникнуть к нему в будующем, называть мусором это конечно сильно.
Видимо у нас разные понятия о сущностях или тестах.
В моем понимании сущность это Data Mapper + Rich Domain Model в которую помещается вся бизнес-логика для реализации которой не труебуется взаимодействие с другими сервисами. Таким образом ни для её создания ни для вызова её методов никакие другие сервисы не нужны, и тестировать её можно простым Unit-тестом.
Для тестирования бизнес-логики размещенной в сущности необходимо:
Экземпляра сущности.
Для тестирования бизнес-логики размещенной в сервисе необходимо:
Экземпляр сервиса.
Зависимости сервиса, либо их моки.
Экземпляра сущности или её мок.
И в чем тут простота?
Просто напонмю что поддержка именованых аргументов в PHP появилась 3 года назад и была завезена в симфонийские валидаторы еще с 5.4 версии.
Ctrl + B
Alt + ↓ \ Alt + ↑ неплохо помогают
А я хоть где-то упоминал про полный отказ от мышки?
Отказался я от нее прежде всего при навигации между файлами\папками.
Если подходить к именованию классов и структуре проекта системно - мне куда проще через Ctrl + Shift + N ввести название класса либо его аббревиатуру, чем перемещаться к нему через древо каталогов. И чем больше проект тем более такой подход мне кажется комфортнее.
Как раз таки на маленьких проектиках где все файлы умещаются в окне Project такой подход уже кажется избыточным. При работе с чужими исходниками, пока с ним не ознакомишься мне тоже удобнее гулять мышью. Но большая часть кода с которой приходится работать либо написана мной, либо написана в соответствии с тем же набором правил которому следую я, что тоже способствует быстрому пониманию структуры.
В котором до сих пор ломается что-то что отлично работает в старом интерфесе.
Или сложнее. У меня регулярно перестают работать хоткеи перехода в пункты меню через Alt + {FirstLetter}, в итоге вместо отрытия меню Git нажатием Alt + G, мне приходится перебирать хоткеи других пунктов и уже после нахождения работающего переходить в Git.
Начну с того что не пытаюсь никого переубедить в том кому как удобней. Вопрос привычек, предпочтений и опыта работы с конкретной конфигурацией.
Да. Я никогда эти шорткаты не заучивал. Увидел -> решил что это удобней чем навигация мышкой -> начал использовать. Пол года назад обнаружил что табы вообще не использую и выключил их.
Вместо того чтобы тянутся к мышке, а потом обратно, я жму клавиши которые вот уже под руками. Когнитивной нагрузки это создает не больше чем нажать F для очередного foreach.
Ctrl + E открывает похожий список с возможность фильтрации по названию.
Тоже отказался от табов в пользу Ctrl + N \ Ctrl + E \ Ctrl + Tab \ Ctrl + Alt + Right \ Ctrl + Alt + Left
Как только:
https://github.com/docker-library/php/pull/1464
так сразу
Умер