А вы подумали, зачем вам AR от базы отвязывать? Чего именно вы этим добиться хотите и надо ли оно вашим работодателям?
Рецепт от меня:
1. AR юзаем как слой доступа к данным, без бизнес-логики. Валидацию там используем для проверки соответствия модели данных. Хорошо сделать в AR метод типа saveOrFail(), который выбрасывает исключение в случае ошибки валидации. Валидацию данных извне делаем только в слое форм. Никакого наследования AR. AR представляет в приложении строки из БД как они есть.
2. Всю бизнес-логику в сервисы. Сервисы покрываем юнит тестами. Другие сервисы запрашиваем через конструктор делаемого сервиса. AR создаем в этих сервисах через фабрику (без new и статики внутри сервисов, чтобы можно было мокать), либо передаем в качестве аргументов метода:
Код: Выделить всё
public function actionDoFoo(string $id, FooService $service)
{
$bar = Bar::findOne($id);
if ($model === null) {
throw new NotFoundHttpException();
}
$service->doFoo($bar);
}
Конструктор контроллера необязательно переопределять, вроде вернули внедрение через action. Сами контроллеры делаем максимально простыми: авторизация, http-фильтрация, вызов валидации форм, Rate limits, вызов сервисов, вызов представлений.
3. Сервисы регистрируем в контейнере внедрения. Интерфейсы для сервисов без острой необходимости не делаем.
4. Если между сервисами появляется дублирование, выносим дублирующийся код в новый сервис, т.е. применяем композицию. Наследование сервисов применяем в крайнем случае.
5. DTO это нормально в качестве контейнера для передачи данных между слоями, например из формы в сервис данные передать. Особенно доставляет при появляении типизированных свойств в php 7.4.
В итоге получим приложение вокруг контейнера. В принципе в SF то же самое получается (приложение вокруг контейнера), если только не делать RichDomainModel. По моим наблюдениям, сейчас люди возвращаются к AnemicDomainModel, юзая сущности Doctrine с аннотациями как слой доступа к данным, что, на мой взгляд, правильно. Рашьше я думал по-другому. Так можно писать и на Yii и на SF и на Laravel и на CakePHP, да хоть на Laminas =), хоть с DataMapper (DataMapper лишь облегчит преобразование типов и тестирование), хоть c AR. Универсальный подход.
Модуль в качестве контейнера тоже иногда использую, неплохая практика, когда сервис только внутри модуля нужен.
Сейчас работаю на SF, там своих нюансов, магии и говнокода хватает. Не надо думать что в SF всё так радужно. Doctrinе ORM, в отличие от Yii накладывает свои ограничения на дизайн БД, фактически подталкивая к созданию суррогатных ключей для всех сущностей. Особенно это чувствуется при использовании внешнего ключа в качестве части первичного. Если юзаешь доктрину, то старая схема, пусть даже очень хорошая, под нее может не подойти. Это ограничивает рефакторинг старых приложений на SF. Безусловно, DataMapper имеет свои преимущества, а именно возможность сменить СУБД и более простое тестирование.
P.S.
Конечно, yii давно не выпускют мажорной версии, для нового проекта лучше что-то другое. Тем не менее, архитектура веб-приложений на сервере особо и не изменилась с 2014 года. В основном фронтенд поменялся, ну еще GraphQL обороты набирает. Мода на гексагон и фреймоврконезависимость, думаю, пройдет. Одно время все с шиной команд и запросов носились, где сейчас это всё? =) Слабая сторона Yii это фронтенд и виджеты, но можно систему ассетов yii выкинуть и сделать свою сборку на webpack и все нормально будет. Также очень не нравится использоване AR в качестве REST-ресурсов, но это тоже можно обойти.
Это мое скромное мнение, как практикующего разработчика. Я ничего не продаю и не рекламирую.