Слоистая архитектура для Yii приложений
Слоистая архитектура для Yii приложений
Хочу вынести на суд сообщества свою статью о том как организовать слоистую архитектуру в Yii приложениях. Это не DDD в чистом виде, но зато мне кажется такое разделение легче понять и "положить" на MVC фреимворки. Тема спорная, как и все в архитектуре и только в спорах рождается истина С удовольствием бы послушала аргументы и советы. А что вы реально применяете в своих типовых проектах (без большой и явной доменной зоны) ?
PS: статья на англ, перевод выложу на днях у себя в блоге.
PS: статья на англ, перевод выложу на днях у себя в блоге.
Re: Слоистая архитектура для Yii приложений
UserRepository получился с состоянием, что весьма странно.
Re: Слоистая архитектура для Yii приложений
Видимо, там имеются ввиду finder insances, как в yii1: CActiveRecord::model()
В целом статья понравилась. Данный подход уже был известен сообществу Yii. В сообществе Laravel про него полно материалов, в yii про него говорили nepster и SamDark еще несколько лет назад. Статья делает его небольшой обзор.
Проблемы такого подхода так же известны - ограничение наследованием от AR, зависимость домена от фреймворков, необходимость не забывать и соблюдать дисциплину: не вызывать AR::save() и прочие database-методы нигде, кроме репозиториев, зависимость от типов данных БД. Это основное. Если что-то забыл, коллеги поправят)
UPD: про невозможность чистых модульных тестов читайте далее.
Если хотите нормальное модульное тестирование - читайте статьи Дмитрия Елисеева.
Про dto и декораторы для представлений - весьма полезно. Представления будут практически чистыми от логики.
Последний раз редактировалось anton_z 2017.04.08, 02:55, всего редактировалось 3 раза.
- samdark
- Администратор
- Сообщения: 9489
- Зарегистрирован: 2009.04.02, 13:46
- Откуда: Воронеж
- Контактная информация:
Re: Слоистая архитектура для Yii приложений
В общем понравилась статья. Не так сложно, как полноценный DDD-подход, но плюсы ощутимы сразу даже на относительно несложных проектах. Примерно так у нас было всё организовано в Stay.com.
Как ElisDN выше отметил, репозиторию экземпляр модели User не нужен. Про дисциплину anton_z тоже верно отметил. Если бы у нас в команде было больше программистов и не я был бы самым не опытным, скорее всего было бы худо.
Часть с DTO и форматированием в нём понравилась. Дам почитать @Dynasource из команды Yii. Он как раз у себя подобное на проекте сделал.
Как ElisDN выше отметил, репозиторию экземпляр модели User не нужен. Про дисциплину anton_z тоже верно отметил. Если бы у нас в команде было больше программистов и не я был бы самым не опытным, скорее всего было бы худо.
Часть с DTO и форматированием в нём понравилась. Дам почитать @Dynasource из команды Yii. Он как раз у себя подобное на проекте сделал.
Нравится Yii? Давайте сделаем его лучше!.
Re: Слоистая архитектура для Yii приложений
Спасибо Даже не ожидала что помидоры не полетят
Про дисциплину согласна на все 100%, но без этого никуда.
Вот кстати ссылка на перевод статьи.
Одна из первых тем про DDD на этом форуме была моя, года 2 назад. Так что да, нового ничего нет, просто попытка собрать и структурировать мысли. Кстати не могу сказать что в Laravel такая структура является общепринятой, в default app нет даже папки для моделей зато есть файл мусорного хелпера
Подсмотрено в Ruby on Rails.
Идея была в том, чтобы отделить AR модель от репозитория, но да, пример кода в статье не использует инъектируемую модель. В реальном проекте это выглядит примерно так: $this->productModel->find()->... Помогает более логично делить ответственность между репозиториями, как минимум контролировать сколько моделей использует один репозиторий. И если нужно добавить в него 6-10-ю модель - значит репозиторий пора делить или метод помещать туда, где нужные модели уже имеются. У меня далеко не с первого раза получается правильно поделить отвественность между репозиториями. Инъекции помогают видить/измерить объем проблемы.
Про дисциплину согласна на все 100%, но без этого никуда.
Вот кстати ссылка на перевод статьи.
Re: Слоистая архитектура для Yii приложений
Извините, наверное что-то пропустил)
По моим впечатлениям, в yii в принципе нужно больше дисциплины и соглашений чем в том же symfony - Yii::app() везде доступен и им часто злоупотребляют. Сколько видел этого в beforeSave и afterSave.
Re: Слоистая архитектура для Yii приложений
ну если хотите. по-моему здесь только идея хорошая, но как говорится, благими намерениями выстлана дорога в ад.
Много статики, много магии, много AR. Последних двух вообще не должно быть, т.к. магия делаем менее очевидным и друно пахнущим сам код, а AR собственно не дает осуществить саму цель слоистой архитектуры - изоляция слоев друг от друга.
речь не про дефолтную структуру, а про популярность темы архитектуры в сообществе ларавел.Roksalana писал(а): ↑2017.04.02, 10:14Одна из первых тем про DDD на этом форуме была моя, года 2 назад. Так что да, нового ничего нет, просто попытка собрать и структурировать мысли. Кстати не могу сказать что в Laravel такая структура является общепринятой, в default app нет даже папки для моделей зато есть файл мусорного хелпера
все придумал фаулер в дремучие годы. dto - паттерн общего предназначения. декораторы вью - это ViewModel зенда (в ларавеле есть presenter'ы?) - помнится, около года-полутора назад в одной из веток я предложил использование viewmodel для инкапсуляции данных и функций, но в обширном сабтреде меня никто не поддержал - избыточность кода, видите ли.
использование AR в yii-проекте требует практически невероятной дисциплины. Шанс, что AR кем-то будет использован напрямую в проекте из 5 человек - 100% в пределах разумного временного промежутка.
Re: Слоистая архитектура для Yii приложений
При таком подходе кол-во статики, которую нельзя замокать - минимально, магии тоже нет или вы про __get?zelenin писал(а): ↑2017.04.02, 14:07 ну если хотите. по-моему здесь только идея хорошая, но как говорится, благими намерениями выстлана дорога в ад.
Много статики, много магии, много AR. Последних двух вообще не должно быть, т.к. магия делаем менее очевидным и друно пахнущим сам код, а AR собственно не дает осуществить саму цель слоистой архитектуры - изоляция слоев друг от друга.
AR остается только на уровне репозитория. Верхнии слои о нем не знают.
Кодревью раз, бить по рукам два, а если честно, иногда нужно сделать быстро и тогда да, начинаешь говнокодить, но важно понимать что технический долг никуда не исчезнет сам по себе и как появляется время - нужно чистить. Вообще чистить код, имхо нужно постоянно, какой бы крутой команда не была (хотя может я просто еще не работала в действительно крутой команде)
Спасибо за мнение
PS: Insolita в блоге подсказала что я совсем забыла написать про валидацию
- samdark
- Администратор
- Сообщения: 9489
- Зарегистрирован: 2009.04.02, 13:46
- Откуда: Воронеж
- Контактная информация:
Re: Слоистая архитектура для Yii приложений
В действительно крутых тоже друг за другом чистят постоянно. Иногда ещё и носом тыкают "в шутку".
Нравится Yii? Давайте сделаем его лучше!.
Re: Слоистая архитектура для Yii приложений
Так а в вашей статье сервисный слой знает же о AR:
Код: Выделить всё
public function user (Request $request)
{
$user = $this->userService->getUserById($request->id); // 1)
$user = DTO::make($user); // 2)
return view('user.index', compact('user')); // 3)
}
"Часто в контроллер возвращают AR модель" (c).
$user - это у вас AR.
Раз сервисный слой вернул AR значит он о ней знает.
А вот уже дальше, чтобы контроллеры и шаблоны не знали о AR
2) Создаем DTO класс, тоже знающий о AR, но только как о чистых данных(stdClass).
И этот DTO "представляет бизнес модель"(c), хотя тут не совсем ясно в каком бизнесе он далее будет участвовать, там уже на представление дело идет (3), а все бизнес процессы вроде на шаге 1) должны были отработать
Re: Слоистая архитектура для Yii приложений
Читайте в оригинале. Перевод не очень.
В этой методологии дело не в расслоении. Изолированных слоев при таком подходе добиться не удастся, он целиком построен на дисциплине. Вернуть AR из сервиса в контроллер - само по себе не беда. Главное не вызывать save(). Но это уже лучше чем делать все "как попало", как любят делать многие. Этот подход позволяет изолированно тестировать сервисы - основную логику. В этом его фишка.
В этой методологии дело не в расслоении. Изолированных слоев при таком подходе добиться не удастся, он целиком построен на дисциплине. Вернуть AR из сервиса в контроллер - само по себе не беда. Главное не вызывать save(). Но это уже лучше чем делать все "как попало", как любят делать многие. Этот подход позволяет изолированно тестировать сервисы - основную логику. В этом его фишка.
Re: Слоистая архитектура для Yii приложений
А где его (save()) вызывать-то?anton_z писал(а): ↑2017.04.05, 15:20 Читайте в оригинале. Перевод не очень.
В этой методологии дело не в расслоении. Изолированных слоев при таком подходе добиться не удастся, он целиком построен на дисциплине. Вернуть AR из сервиса в контроллер - само по себе не беда. Главное не вызывать save(). Но это уже лучше чем делать все "как попало", как любят делать многие. Этот подход позволяет изолированно тестировать сервисы - основную логику. В этом его фишка.
Re: Слоистая архитектура для Yii приложений
Оригинал только у меня не открывается?
Интересно было бы увидеть пример реализации чего-то реального на этой архитектуре (на гитхабе). Например, простенького интернет магазина/каталога.Превышено время ожидания ответа от сайта toptal.com.
Re: Слоистая архитектура для Yii приложений
Вот вы не поверите, но изучая многие ветки этого форума становится понятно, что открытых проектов на такой слоистой архитектуре, правильной, практически нет =)
Сам в поисках примера и приходится перелопачивать кучу кода, отсеивая шлак и забирая самое интересное\красивое на мой взгляд решение.
И пока я изучаю интенсив по ООП с полной уверенностью, что смогу найти ответы на свои вопросы, пока все же проблема конструирования правильной архитектуры остается.
Так вот основной вопрос, конечно в том, как все это дело применять. Лично у меня проблема с расстановкой все по своим местам. Вы написали про пример "интернет-магазин", давайте совместно моделировать такой пример.
Потом, конечно, когда все станет понятно, можно будет посмеяться от глупости вопросов.
Я начну как я это понимаю (буду использовать в примера DI от Yii2 но понимаю, что от него нужно отвязываться).
Получается, что в работе у нас участвуют:
1. Контрллер, в котором происходит взаимодействие с сервисным слоем
2. Сервисный слой
3. Некий репозиторий для соединения сервисного слоя и сущности (например, заказ)
4. Сущность
Сервисный слой, которым я буду пользоваться везде, где мне нужно, в контроллерах, например.
Код: Выделить всё
namespace app\services;
class OrderService {
private $orderRepository;
public function __construct(\app\repositories\OrderRepository $orderRepository) {
$this->orderRepository = $orderRepository;
}
}
Далее необходимо описать интерфес для репозитория, сам репозиторий и сущность.
Код: Выделить всё
namespace app\interfaces;
interface OrderRepositoryInterface {
public function findOne($id);
}
Код: Выделить всё
namespace app\repositories;
use app\interfaces\OrderRepositoryInterface;
class OrderRepository implements OrderRepositoryInterface {
public function findOne($id) {
return \app\models\Order::findOne( $id );
}
}
Дальше реализую метод в сервисном слое для доступа к данным, например так
Код: Выделить всё
public function getById($id) {
return $this->orderRepository->findOne( $id );
}
А уже в контрллере я могу сделать что-то вроде
Код: Выделить всё
$order = \Yii::$app->order->getById( 43 );
if( $order->isActive() ) {
\Yii::$app->order->completeOrder($order);
}
Re: Слоистая архитектура для Yii приложений
vitovt, в правильном направлении идете.
Вызывать save у AR по данной методологии нужно в репозитории:
Вызывать save у AR по данной методологии нужно в репозитории:
Код: Выделить всё
class OrderRepo
{
public function save(Order $order)
{
$order->save(false);
}
}
Re: Слоистая архитектура для Yii приложений
Крупных проектов в открытом доступе почти нет, так как практически ни одна компания исходники своих реальных проектов не выкладывает. А проектов с какой-либо архитектурой ещё меньше.
Напишете потом к нему честный отзыв?
Re: Слоистая архитектура для Yii приложений
Все верно, конвертировать в DTO нужно на уровне сервиса до того, как вернуть данные в контроллер. Пример в статье упрощенный, но не правильный с этой точки зрения.maleks писал(а): ↑2017.04.05, 14:57 Так а в вашей статье сервисный слой знает же о AR:1) Сервисный слой - "Здесь и только здесь должна быть информация о бизнес процессах и взаимосвязях между бизнес моделями" (c)Код: Выделить всё
public function user (Request $request) { $user = $this->userService->getUserById($request->id); // 1) $user = DTO::make($user); // 2) return view('user.index', compact('user')); // 3) }
"Часто в контроллер возвращают AR модель" (c).
$user - это у вас AR.
Раз сервисный слой вернул AR значит он о ней знает.
А вот уже дальше, чтобы контроллеры и шаблоны не знали о AR
2) Создаем DTO класс, тоже знающий о AR, но только как о чистых данных(stdClass).
И этот DTO "представляет бизнес модель"(c), хотя тут не совсем ясно в каком бизнесе он далее будет участвовать, там уже на представление дело идет (3), а все бизнес процессы вроде на шаге 1) должны были отработать
Re: Слоистая архитектура для Yii приложений
Для начала думаю сделать base app в таком стиле, как будет свободное время. Писать так сложно и становится реально нужно когда есть большая кодовая база и по другому уже будет каша из говнокода. Поэтому никто не пишет так простые проекты/модули, а большие проекты не выкладываются в паблик.
Re: Слоистая архитектура для Yii приложений
Супер! И тут же в репозитории должны быть все действия над объектом? Сделать активным \ неактивным? Записать историю в лог-таблицу к примеру? Все, что касается изменений "заказа" все в репозиторий?anton_z писал(а): ↑2017.04.06, 01:36 vitovt, в правильном направлении идете.
Вызывать save у AR по данной методологии нужно в репозитории:
Код: Выделить всё
class OrderRepo { public function save(Order $order) { $order->save(false); } }
Re: Слоистая архитектура для Yii приложений
Имхо, вызывать getId() в сервисном слое - плохая идея, т.к по всем сервисам расползутся вызовы AR модели. Если может репозиторий вернуть эти данные - пусть сразу возвращает. Если нет - то инкапсулировать в DTO объект (можно сразу несколько AR моделей передавать) и в нем собирать поля через getId(), getClientId(), сервис будет работать с этим объектом и ему все равно как именно поле расчитано. Завтра clientId будет перемещен (условно) в другую модель - поменяете один метод в нужно DTO и все работает дальше.
В контроллере вызываем методы AR модели + та самая "бизнес логика" Это то от чего нужно уходить. Если от состояния модели зависят дальнейщие действия - это то о чем знает сервис: $order->isActive() => completeOrder - это пример "бизнес логики" в моем понимании. В контроллере получили данные (почистили, преобразовали и тп), передали сервисному слою, получили ответ (почистили, преобразовали и тп если надо) - вернули то что от нас ждут (html, json, xml и тп)vitovt писал(а): ↑2017.04.05, 22:53 А уже в контрллере я могу сделать что-то вроде
Код: Выделить всё
$order = \Yii::$app->order->getById( 43 ); if( $order->isActive() ) { \Yii::$app->order->completeOrder($order); }