Здравствуйте, всем! Подскажите, пожалуйста, кто нибуть занимался, применял на практике слоистую архитектуру построения на YII2. Может есть у кого то примеры как все это построить, где и что создавть слой сервиса, слой репозитория, завистимости... все так вообщем расписано а вот на приктических примерах особо нет...
https://yiiframework.ru/forum/viewtopic.php?t=37556 https://yiiframework.ru/forum/viewtopic.php?t=43009
Спасибо за инфо, а такой встречный вопрос: создали слой сервис, слой репозиторий (хранилище), в самом слое репозиторий как лучше создавать запросы ( использовать Active Records для работы с БД или все же лучше придерживаться Yii2 DAO (объекты доступа к данным) - без использование AR?
Получается ActiveRecord при таком подходе (слоистая архитектура) вообще не следует нигде использовать. Ясно. Извиняюсь, еще один вопрос к вам: получается так, что когда создаем контроллер и список действий в нем, к нему создаем очередной слой сервиса + слой репозитория (соответвенно проводя регистрацию зависимостей слоев в контейнере). И так каждый раз при создании новых контроллеров? А вот получается, сколько таблиц у нас в БД столько и слоев сервисов и репозиториев будет - на 1 табл - 1 слой репозитория, верно понимаю? спасибо.
Да, в идеале эта архитектура подразумевает в доменной модели написание полностью очищенного от "железа" кода. Получается полностью фреймворконезависимое ядро, подключающееся ко внешнему миру (к контроллерам, БД и прочей фреймворковской обвязке) через инфраструктурный связующий слой. В реальности 100% независимость делают не всегда. Чтобы не велосипедить, часто берут Doctrine и мирятся с её особенностями. Слои горизонтальные, а не вертикальные. И да, для каждого агрегата нужен будет свой репозиторий и свои сервисы.
А как быть, вернее как правильно сделать, есть sql запрос в репозитории к примеру: Код (Text): namespace app\storage; class BoardDaoStorage implements StorageInterface { const TABLE_NAME = 'board'; private $connection; public function __construct() { $this->connection = \Yii::$app->db; } public function load() { return $this->connection->createCommand('SELECT * FROM ' . self::TABLE_NAME . ' ')->queryAll(); } а нужно сделать выборку из 2-х таблиц, а мы придерживаемся концепсии: 1 табл - 1 слой репозитория - как правильно прописать вторую табл.
Нет, такой концепции нет. Концепция – один репозиторий на один агрегат, а не на таблицу. В агрегате Employee из примеров по второй ссылке имеется куча внутренностей вроде наборов Phone и Status, раскладываемых по своим таблицам. И в репозитории может быть метод getByPhone($phone), делающий SELECT e.* FROM employees e INNER JOIN phones p WHERE p.number = :number LIMIT 1. Так что в репозитории спокойно делаем запросы на любое число таблиц, лишь бы правильно сохранить и достать сущность. А для отображения на сайте доменные сущности неудобны, если нужно к постам JOIN-ить их авторов и число комментариев. Для таких целей удобно сделать отдельный ReadRrpository вроде https://yiiframework.ru/forum/viewtopic.php?f=34&t=42014&p=207524#p207524, который будет делать любые запросы и возвращать наборы DTO или массивы.
Потому что практическая ценность данного подхода сомнительна. Вы создаете ещё один (или даже не один) слой абстракции, который в теории даст вам возможность отвязать приложение от фреймворка, но при этом значительно повысит трудозатраты на написание и поддержание, а так же увеличит порог входа, т.к. вместо использования общеизвестного функционала вы фактических построите свой вокруг него. Это в лучшем случае, в реальности же отвязка будет далеко не полной. А учитывая, что веб - живая среда, где подходы и тренды меняются довольно быстро, не говоря уже о изменяющихся бизнес-требованиях, возникает вопрос: стоит ли всё это затраченных усилий? Чисто как академическое упражнение - да, стоит, в остальных случаях это применимо не больше, чем rocket science в дешевом смартфоне: заморочиться можно, но во-первых он резко перестанет быть дешевым, а во-вторых, зачем всё это звонилке, которую просто следует взять и заменить на другую боле свежую? ) А вот сервисы / репозитории - нужны, но не как средство абстракции, а как способ структурирования кода приложения. имхо
Вы должно быть шутите. Разделение приложения на слои - это основа хорошей архитектуры или чистой как ее называет дядя Боб. А вот как это делать уже вопрос подхода. Есть луковые, гексогональные, чистые и т.д. архитектуры. Но последователи архитектурных идей тут перегибают и начинают топить именно за "фреймворконезависимость", как самоцель. Хотя чистые архитектуры не требуют этого а просто говорят что фреймворк и его вещи должны использоваться на внешних слоях (а не как в yii везде где хочешь Yii::$app->component вкатал и ура).
Возник однин еще вопрос: Конфигурация зависимостей в DI Container yii2. Создадаю файл предварительной загрузки для регистрации зависимостей в di контейнире. Для этого в папке config создадаю, к примеру файл - BoardBootstrap.php который обязательно должен реализовать BootstrapInterface: вот пример: Код (Text): class BoardBootstrap implements BootstrapInterface { public function bootstrap($app) { $container = \Yii::$container; $container->setSingleton('BoardService'); $container->set('app\storage\StorageInterface', function () { return new BoardDaoStorage(Yii::$app->db); }); } } Здесь я регистрирую BoardDaoStorage (репозиторий / хранилище) в контейнере зависимостей а Сервис BoardService регистрируем через одиночку setSingleton('BoardService') так как нам нет необходимости создавать разные объекты этого класса. Вопрос в следующем: есть один слой сервиса + слой репозитория для одного контролера. Если я добавляю новый слой и новое хранилище - я снова все это регистрирую в di контейнире? Все вновь созданые слои и репозитории нужно регестриовать таким образом, правильно я это понимаю?
В простейшем случае зарегистрируйте хотя бы только соответствия интерфейсам: PHP: $container->setSingleton(Storage::class, function () { return new BoardDaoStorage(Yii::$app->db); }); Остальное всё подтянется автоматически через рефлексию. Контейнер Yii неудобен тем, что не использует setSingleton по умолчанию.
А слои сервисов получается не нужно вообще регестрировать здесь? Регестрируем только все созданые репозитории таким способом: Код (Text): $container->setSingleton(Storage-1::class, function () { return new BoardDaoStorage(Yii::$app->db); }); $container->setSingleton(Storage-2::class, function () { return new BoardDaoStorage(Yii::$app->db); }); $container->setSingleton(Storage-3::class, function () { return new BoardDaoStorage(Yii::$app->db); }); и т .д. Верно я вас понимаю? Слои сами подтянутся?
Обязательно только для интерфейсов: PHP: interface Storage { ... } $container->setSingleton(Storage::class, function () use ($app) { return new BoardDaoStorage($app->db); }); и нестандартных конструкторов: PHP: $container->setSingleton(Notifier::class, function () use ($app) { return new Notifier($app->mailer, $app->params['notify_email']); }); А контроллеры и сервисы можно не вписывать. Они сами автоматически подтянутся.