За последние 24 часа нас посетили 22045 программистов и 1105 роботов. Сейчас ищут 568 программистов ...

Слоистая архитектура для Yii приложений

Тема в разделе "Yii", создана пользователем Alex76, 22 июл 2018.

  1. Alex76

    Alex76 Новичок

    С нами с:
    12 июл 2018
    Сообщения:
    9
    Симпатии:
    0
    Здравствуйте, всем!

    Подскажите, пожалуйста, кто нибуть занимался, применял на практике слоистую архитектуру построения на YII2. Может есть у кого то примеры как все это построить, где и что создавть слой сервиса, слой репозитория, завистимости... все так вообщем расписано а вот на приктических примерах особо нет...
     
  2. ElisDN

    ElisDN Активный пользователь

    С нами с:
    13 фев 2018
    Сообщения:
    605
    Симпатии:
    130
  3. Alex76

    Alex76 Новичок

    С нами с:
    12 июл 2018
    Сообщения:
    9
    Симпатии:
    0
    Спасибо за инфо, а такой встречный вопрос: создали слой сервис, слой репозиторий (хранилище), в самом слое репозиторий как лучше создавать запросы ( использовать Active Records для работы с БД или все же лучше придерживаться Yii2 DAO (объекты доступа к данным) - без использование AR?
     
  4. ElisDN

    ElisDN Активный пользователь

    С нами с:
    13 фев 2018
    Сообщения:
    605
    Симпатии:
    130
    В идеальном случае – через DAO, без ActiveRecord.
     
  5. Alex76

    Alex76 Новичок

    С нами с:
    12 июл 2018
    Сообщения:
    9
    Симпатии:
    0
    Получается ActiveRecord при таком подходе (слоистая архитектура) вообще не следует нигде использовать.
    Ясно. Извиняюсь, еще один вопрос к вам: получается так, что когда создаем контроллер и список действий в нем, к нему создаем очередной слой сервиса + слой репозитория (соответвенно проводя регистрацию зависимостей слоев в контейнере). И так каждый раз при создании новых контроллеров?
    А вот получается, сколько таблиц у нас в БД столько и слоев сервисов и репозиториев будет - на 1 табл - 1 слой репозитория, верно понимаю?
    спасибо.
     
  6. ElisDN

    ElisDN Активный пользователь

    С нами с:
    13 фев 2018
    Сообщения:
    605
    Симпатии:
    130
    Да, в идеале эта архитектура подразумевает в доменной модели написание полностью очищенного от "железа" кода. Получается полностью фреймворконезависимое ядро, подключающееся ко внешнему миру (к контроллерам, БД и прочей фреймворковской обвязке) через инфраструктурный связующий слой.

    В реальности 100% независимость делают не всегда. Чтобы не велосипедить, часто берут Doctrine и мирятся с её особенностями.

    Слои горизонтальные, а не вертикальные. И да, для каждого агрегата нужен будет свой репозиторий и свои сервисы.
     
    #6 ElisDN, 22 июл 2018
    Последнее редактирование: 22 июл 2018
  7. Alex76

    Alex76 Новичок

    С нами с:
    12 июл 2018
    Сообщения:
    9
    Симпатии:
    0
    А как быть, вернее как правильно сделать, есть sql запрос в репозитории к примеру:

    Код (Text):
    1. namespace app\storage;
    2.  
    3. class BoardDaoStorage implements StorageInterface
    4. {
    5.     const TABLE_NAME = 'board';
    6.  
    7.     private $connection;
    8.  
    9.     public function __construct()
    10.     {
    11.         $this->connection = \Yii::$app->db;
    12.  
    13.     }
    14.  
    15.     public function load()
    16.     {      
    17.         return $this->connection->createCommand('SELECT * FROM ' . self::TABLE_NAME . ' ')->queryAll();
    18.     }
    а нужно сделать выборку из 2-х таблиц, а мы придерживаемся концепсии: 1 табл - 1 слой репозитория - как правильно прописать вторую табл.
     
  8. ElisDN

    ElisDN Активный пользователь

    С нами с:
    13 фев 2018
    Сообщения:
    605
    Симпатии:
    130
    Нет, такой концепции нет. Концепция – один репозиторий на один агрегат, а не на таблицу. В агрегате 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 или массивы.
     
  9. Alex76

    Alex76 Новичок

    С нами с:
    12 июл 2018
    Сообщения:
    9
    Симпатии:
    0
    Спасибо!
     
    #9 Alex76, 22 июл 2018
    Последнее редактирование: 23 июл 2018
  10. romach

    romach Старожил

    С нами с:
    26 окт 2013
    Сообщения:
    2.904
    Симпатии:
    719
    Потому что практическая ценность данного подхода сомнительна.

    Вы создаете ещё один (или даже не один) слой абстракции, который в теории даст вам возможность отвязать приложение от фреймворка, но при этом значительно повысит трудозатраты на написание и поддержание, а так же увеличит порог входа, т.к. вместо использования общеизвестного функционала вы фактических построите свой вокруг него. Это в лучшем случае, в реальности же отвязка будет далеко не полной. А учитывая, что веб - живая среда, где подходы и тренды меняются довольно быстро, не говоря уже о изменяющихся бизнес-требованиях, возникает вопрос: стоит ли всё это затраченных усилий? Чисто как академическое упражнение - да, стоит, в остальных случаях это применимо не больше, чем rocket science в дешевом смартфоне: заморочиться можно, но во-первых он резко перестанет быть дешевым, а во-вторых, зачем всё это звонилке, которую просто следует взять и заменить на другую боле свежую? )

    А вот сервисы / репозитории - нужны, но не как средство абстракции, а как способ структурирования кода приложения.

    имхо
     
  11. Дюран

    Дюран Активный пользователь

    С нами с:
    9 мар 2018
    Сообщения:
    257
    Симпатии:
    19
    Вы должно быть шутите.
    Разделение приложения на слои - это основа хорошей архитектуры или чистой как ее называет дядя Боб.
    А вот как это делать уже вопрос подхода. Есть луковые, гексогональные, чистые и т.д. архитектуры.

    Но последователи архитектурных идей тут перегибают и начинают топить именно за "фреймворконезависимость", как самоцель. Хотя чистые архитектуры не требуют этого а просто говорят что фреймворк и его вещи должны использоваться на внешних слоях (а не как в yii везде где хочешь Yii::$app->component вкатал и ура).
     
  12. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.410
    Симпатии:
    1.768
    если позволите, я согласен с вами обоими
     
  13. Alex76

    Alex76 Новичок

    С нами с:
    12 июл 2018
    Сообщения:
    9
    Симпатии:
    0
    Возник однин еще вопрос: Конфигурация зависимостей в DI Container yii2.
    Создадаю файл предварительной загрузки для регистрации зависимостей в di контейнире. Для этого в папке config
    создадаю, к примеру файл - BoardBootstrap.php который обязательно должен реализовать BootstrapInterface: вот пример:

    Код (Text):
    1. class BoardBootstrap implements BootstrapInterface
    2. {
    3.     public function bootstrap($app)
    4.     {
    5.         $container = \Yii::$container;
    6.  
    7.         $container->setSingleton('BoardService');
    8.  
    9.         $container->set('app\storage\StorageInterface', function () {
    10.             return new BoardDaoStorage(Yii::$app->db);
    11.         });
    12.     }
    13. }
    Здесь я регистрирую BoardDaoStorage (репозиторий / хранилище) в контейнере зависимостей а Сервис BoardService регистрируем через одиночку setSingleton('BoardService') так как нам нет необходимости создавать разные объекты этого класса.
    Вопрос в следующем: есть один слой сервиса + слой репозитория для одного контролера.
    Если я добавляю новый слой и новое хранилище - я снова все это регистрирую в di контейнире? Все вновь созданые слои и репозитории нужно регестриовать таким образом, правильно я это понимаю?
     
  14. ElisDN

    ElisDN Активный пользователь

    С нами с:
    13 фев 2018
    Сообщения:
    605
    Симпатии:
    130
    В простейшем случае зарегистрируйте хотя бы только соответствия интерфейсам:

    PHP:
    1. $container->setSingleton(Storage::class, function () {
    2.     return new BoardDaoStorage(Yii::$app->db);
    3. });
    Остальное всё подтянется автоматически через рефлексию.

    Контейнер Yii неудобен тем, что не использует setSingleton по умолчанию.
     
  15. Alex76

    Alex76 Новичок

    С нами с:
    12 июл 2018
    Сообщения:
    9
    Симпатии:
    0
    А слои сервисов получается не нужно вообще регестрировать здесь? Регестрируем только все созданые репозитории таким способом:
    Код (Text):
    1. $container->setSingleton(Storage-1::class, function () {
    2.     return new BoardDaoStorage(Yii::$app->db);
    3. });
    4. $container->setSingleton(Storage-2::class, function () {
    5.     return new BoardDaoStorage(Yii::$app->db);
    6. });
    7. $container->setSingleton(Storage-3::class, function () {
    8.     return new BoardDaoStorage(Yii::$app->db);
    9. });
    и т .д.
    Верно я вас понимаю? Слои сами подтянутся?
     
  16. ElisDN

    ElisDN Активный пользователь

    С нами с:
    13 фев 2018
    Сообщения:
    605
    Симпатии:
    130
    Обязательно только для интерфейсов:

    PHP:
    1. interface Storage {
    2.     ...
    3. }
    4.  
    5. $container->setSingleton(Storage::class, function () use ($app) {
    6.     return new BoardDaoStorage($app->db);
    7. });
    и нестандартных конструкторов:

    PHP:
    1. $container->setSingleton(Notifier::class, function () use ($app) {
    2.     return new Notifier($app->mailer, $app->params['notify_email']);
    3. });
    А контроллеры и сервисы можно не вписывать. Они сами автоматически подтянутся.