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

Обсуждаем, как правильно строить приложения
Аватара пользователя
Roksalana
Сообщения: 213
Зарегистрирован: 2014.01.14, 09:34

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

Сообщение Roksalana » 2017.03.31, 16:12

Хочу вынести на суд сообщества свою статью о том как организовать слоистую архитектуру в Yii приложениях. Это не DDD в чистом виде, но зато мне кажется такое разделение легче понять и "положить" на MVC фреимворки. Тема спорная, как и все в архитектуре и только в спорах рождается истина ;) С удовольствием бы послушала аргументы и советы. А что вы реально применяете в своих типовых проектах (без большой и явной доменной зоны) ?
PS: статья на англ, перевод выложу на днях у себя в блоге.

Аватара пользователя
ElisDN
Сообщения: 5313
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

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

Сообщение ElisDN » 2017.03.31, 18:24

UserRepository получился с состоянием, что весьма странно.

anton_z
Сообщения: 415
Зарегистрирован: 2017.01.15, 15:01

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

Сообщение anton_z » 2017.04.01, 06:37

ElisDN писал(а):
2017.03.31, 18:24
UserRepository получился с состоянием, что весьма странно.
Видимо, там имеются ввиду 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
Администратор
Сообщения: 9101
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

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

Сообщение samdark » 2017.04.01, 21:55

В общем понравилась статья. Не так сложно, как полноценный DDD-подход, но плюсы ощутимы сразу даже на относительно несложных проектах. Примерно так у нас было всё организовано в Stay.com.

Как ElisDN выше отметил, репозиторию экземпляр модели User не нужен. Про дисциплину anton_z тоже верно отметил. Если бы у нас в команде было больше программистов и не я был бы самым не опытным, скорее всего было бы худо.

Часть с DTO и форматированием в нём понравилась. Дам почитать @Dynasource из команды Yii. Он как раз у себя подобное на проекте сделал.

Аватара пользователя
Roksalana
Сообщения: 213
Зарегистрирован: 2014.01.14, 09:34

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

Сообщение Roksalana » 2017.04.02, 10:14

Спасибо :) Даже не ожидала что помидоры не полетят :D
anton_z писал(а):
2017.04.01, 06:37
В целом статья понравилась. Данный подход уже был известен сообществу Yii. В сообществе Laravel про него полно материалов, в yii про него говорили nepster и SamDark еще несколько лет назад. Статья делает его небольшой обзор.
Одна из первых тем про DDD на этом форуме была моя, года 2 назад. Так что да, нового ничего нет, просто попытка собрать и структурировать мысли. Кстати не могу сказать что в Laravel такая структура является общепринятой, в default app нет даже папки для моделей зато есть файл мусорного хелпера
anton_z писал(а):
2017.04.01, 06:37
Про dto и декораторы для представлений - весьма полезно. Представления будут практически чистыми от логики.
Подсмотрено в Ruby on Rails.
samdark писал(а):
2017.04.01, 21:55
Как ElisDN выше отметил, репозиторию экземпляр модели User не нужен. Про дисциплину anton_z тоже верно отметил. Если бы у нас в команде было больше программистов и не я был бы самым не опытным, скорее всего было бы худо.
Идея была в том, чтобы отделить AR модель от репозитория, но да, пример кода в статье не использует инъектируемую модель. В реальном проекте это выглядит примерно так: $this->productModel->find()->... Помогает более логично делить ответственность между репозиториями, как минимум контролировать сколько моделей использует один репозиторий. И если нужно добавить в него 6-10-ю модель - значит репозиторий пора делить или метод помещать туда, где нужные модели уже имеются. У меня далеко не с первого раза получается правильно поделить отвественность между репозиториями. Инъекции помогают видить/измерить объем проблемы.
Про дисциплину согласна на все 100%, но без этого никуда.

Вот кстати ссылка на перевод статьи.

anton_z
Сообщения: 415
Зарегистрирован: 2017.01.15, 15:01

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

Сообщение anton_z » 2017.04.02, 10:47

Roksalana писал(а):
2017.04.02, 10:14
Одна из первых тем про DDD на этом форуме была моя, года 2 назад.
Извините, наверное что-то пропустил)
Roksalana писал(а):
2017.04.02, 10:14
Про дисциплину согласна на все 100%, но без этого никуда.
По моим впечатлениям, в yii в принципе нужно больше дисциплины и соглашений чем в том же symfony - Yii::app() везде доступен и им часто злоупотребляют. Сколько видел этого в beforeSave и afterSave.

zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

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

Сообщение zelenin » 2017.04.02, 14:07

Roksalana писал(а):
2017.04.02, 10:14
Спасибо :) Даже не ожидала что помидоры не полетят :D
ну если хотите. по-моему здесь только идея хорошая, но как говорится, благими намерениями выстлана дорога в ад.
Много статики, много магии, много AR. Последних двух вообще не должно быть, т.к. магия делаем менее очевидным и друно пахнущим сам код, а AR собственно не дает осуществить саму цель слоистой архитектуры - изоляция слоев друг от друга.
Roksalana писал(а):
2017.04.02, 10:14
Одна из первых тем про DDD на этом форуме была моя, года 2 назад. Так что да, нового ничего нет, просто попытка собрать и структурировать мысли. Кстати не могу сказать что в Laravel такая структура является общепринятой, в default app нет даже папки для моделей зато есть файл мусорного хелпера
речь не про дефолтную структуру, а про популярность темы архитектуры в сообществе ларавел.
Roksalana писал(а):
2017.04.02, 10:14
Подсмотрено в Ruby on Rails.
все придумал фаулер в дремучие годы. dto - паттерн общего предназначения. декораторы вью - это ViewModel зенда (в ларавеле есть presenter'ы?) - помнится, около года-полутора назад в одной из веток я предложил использование viewmodel для инкапсуляции данных и функций, но в обширном сабтреде меня никто не поддержал - избыточность кода, видите ли.
Roksalana писал(а):
2017.04.02, 10:14
Про дисциплину согласна на все 100%, но без этого никуда.
использование AR в yii-проекте требует практически невероятной дисциплины. Шанс, что AR кем-то будет использован напрямую в проекте из 5 человек - 100% в пределах разумного временного промежутка.

Аватара пользователя
Roksalana
Сообщения: 213
Зарегистрирован: 2014.01.14, 09:34

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

Сообщение Roksalana » 2017.04.03, 21:22

zelenin писал(а):
2017.04.02, 14:07
ну если хотите. по-моему здесь только идея хорошая, но как говорится, благими намерениями выстлана дорога в ад.
Много статики, много магии, много AR. Последних двух вообще не должно быть, т.к. магия делаем менее очевидным и друно пахнущим сам код, а AR собственно не дает осуществить саму цель слоистой архитектуры - изоляция слоев друг от друга.
При таком подходе кол-во статики, которую нельзя замокать - минимально, магии тоже нет или вы про __get?
AR остается только на уровне репозитория. Верхнии слои о нем не знают.
zelenin писал(а):
2017.04.02, 14:07
использование AR в yii-проекте требует практически невероятной дисциплины. Шанс, что AR кем-то будет использован напрямую в проекте из 5 человек - 100% в пределах разумного временного промежутка.
Кодревью раз, бить по рукам два, а если честно, иногда нужно сделать быстро и тогда да, начинаешь говнокодить, но важно понимать что технический долг никуда не исчезнет сам по себе и как появляется время - нужно чистить. Вообще чистить код, имхо нужно постоянно, какой бы крутой команда не была (хотя может я просто еще не работала в действительно крутой команде)
Спасибо за мнение 8-)
PS: Insolita в блоге подсказала что я совсем забыла написать про валидацию

Аватара пользователя
samdark
Администратор
Сообщения: 9101
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

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

Сообщение samdark » 2017.04.03, 23:37

В действительно крутых тоже друг за другом чистят постоянно. Иногда ещё и носом тыкают "в шутку".

Аватара пользователя
maleks
Сообщения: 1722
Зарегистрирован: 2012.12.26, 12:56

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

Сообщение maleks » 2017.04.05, 14:57

Roksalana писал(а):
2017.04.03, 21:22
AR остается только на уровне репозитория. Верхнии слои о нем не знают.
Так а в вашей статье сервисный слой знает же о 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)
}
1) Сервисный слой - "Здесь и только здесь должна быть информация о бизнес процессах и взаимосвязях между бизнес моделями" (c)
"Часто в контроллер возвращают AR модель" (c).

$user - это у вас AR.
Раз сервисный слой вернул AR значит он о ней знает.
А вот уже дальше, чтобы контроллеры и шаблоны не знали о AR
2) Создаем DTO класс, тоже знающий о AR, но только как о чистых данных(stdClass).
И этот DTO "представляет бизнес модель"(c), хотя тут не совсем ясно в каком бизнесе он далее будет участвовать, там уже на представление дело идет (3), а все бизнес процессы вроде на шаге 1) должны были отработать

anton_z
Сообщения: 415
Зарегистрирован: 2017.01.15, 15:01

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

Сообщение anton_z » 2017.04.05, 15:20

Читайте в оригинале. Перевод не очень.

В этой методологии дело не в расслоении. Изолированных слоев при таком подходе добиться не удастся, он целиком построен на дисциплине. Вернуть AR из сервиса в контроллер - само по себе не беда. Главное не вызывать save(). Но это уже лучше чем делать все "как попало", как любят делать многие. Этот подход позволяет изолированно тестировать сервисы - основную логику. В этом его фишка.

Аватара пользователя
vitovt
Сообщения: 210
Зарегистрирован: 2012.03.21, 10:37
Контактная информация:

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

Сообщение vitovt » 2017.04.05, 20:09

anton_z писал(а):
2017.04.05, 15:20
Читайте в оригинале. Перевод не очень.

В этой методологии дело не в расслоении. Изолированных слоев при таком подходе добиться не удастся, он целиком построен на дисциплине. Вернуть AR из сервиса в контроллер - само по себе не беда. Главное не вызывать save(). Но это уже лучше чем делать все "как попало", как любят делать многие. Этот подход позволяет изолированно тестировать сервисы - основную логику. В этом его фишка.
А где его (save()) вызывать-то?

Аватара пользователя
Faenir
Сообщения: 292
Зарегистрирован: 2010.01.06, 01:46
Откуда: Симферополь

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

Сообщение Faenir » 2017.04.05, 22:14

Оригинал только у меня не открывается?
Превышено время ожидания ответа от сайта toptal.com.
Интересно было бы увидеть пример реализации чего-то реального на этой архитектуре (на гитхабе). Например, простенького интернет магазина/каталога.

Аватара пользователя
vitovt
Сообщения: 210
Зарегистрирован: 2012.03.21, 10:37
Контактная информация:

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

Сообщение vitovt » 2017.04.05, 22:53

Faenir писал(а):
2017.04.05, 22:14
Оригинал только у меня не открывается?
Превышено время ожидания ответа от сайта toptal.com.
Интересно было бы увидеть пример реализации чего-то реального на этой архитектуре (на гитхабе). Например, простенького интернет магазина/каталога.
Вот вы не поверите, но изучая многие ветки этого форума становится понятно, что открытых проектов на такой слоистой архитектуре, правильной, практически нет =)
Сам в поисках примера и приходится перелопачивать кучу кода, отсеивая шлак и забирая самое интересное\красивое на мой взгляд решение.

И пока я изучаю интенсив по ООП с полной уверенностью, что смогу найти ответы на свои вопросы, пока все же проблема конструирования правильной архитектуры остается.

Так вот основной вопрос, конечно в том, как все это дело применять. Лично у меня проблема с расстановкой все по своим местам. Вы написали про пример "интернет-магазин", давайте совместно моделировать такой пример.

Потом, конечно, когда все станет понятно, можно будет посмеяться от глупости вопросов.

Я начну как я это понимаю (буду использовать в примера 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 );
    }
}
Вопрос: могу ли я в данном случае репозиторием возвращаться AR в котором будут определены методы getId(), getClientId() и так далее, верно? Пока что, на старте, потом пытаться отойти от этого.

Дальше реализую метод в сервисном слое для доступа к данным, например так

Код: Выделить всё

  public function getById($id) {
        return $this->orderRepository->findOne( $id );
    }
верно?

А уже в контрллере я могу сделать что-то вроде

Код: Выделить всё

$order = \Yii::$app->order->getById( 43 );

if( $order->isActive() ) {
  \Yii::$app->order->completeOrder($order);
}

anton_z
Сообщения: 415
Зарегистрирован: 2017.01.15, 15:01

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

Сообщение anton_z » 2017.04.06, 01:36

vitovt, в правильном направлении идете.
Вызывать save у AR по данной методологии нужно в репозитории:

Код: Выделить всё


class OrderRepo 
{
	public function save(Order $order) 
	{
		$order->save(false);
	}
}


Аватара пользователя
ElisDN
Сообщения: 5313
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

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

Сообщение ElisDN » 2017.04.06, 07:47

vitovt писал(а):
2017.04.05, 22:53
Вот вы не поверите, но изучая многие ветки этого форума становится понятно, что открытых проектов на такой слоистой архитектуре, правильной, практически нет =)
Крупных проектов в открытом доступе почти нет, так как практически ни одна компания исходники своих реальных проектов не выкладывает. А проектов с какой-либо архитектурой ещё меньше.
vitovt писал(а):
2017.04.05, 22:53
И пока я изучаю интенсив по ООП с полной уверенностью, что смогу найти ответы на свои вопросы, пока все же проблема конструирования правильной архитектуры остается.
Напишете потом к нему честный отзыв?

Аватара пользователя
Roksalana
Сообщения: 213
Зарегистрирован: 2014.01.14, 09:34

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

Сообщение Roksalana » 2017.04.06, 07:58

maleks писал(а):
2017.04.05, 14:57
Так а в вашей статье сервисный слой знает же о 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)
}
1) Сервисный слой - "Здесь и только здесь должна быть информация о бизнес процессах и взаимосвязях между бизнес моделями" (c)
"Часто в контроллер возвращают AR модель" (c).

$user - это у вас AR.
Раз сервисный слой вернул AR значит он о ней знает.
А вот уже дальше, чтобы контроллеры и шаблоны не знали о AR
2) Создаем DTO класс, тоже знающий о AR, но только как о чистых данных(stdClass).
И этот DTO "представляет бизнес модель"(c), хотя тут не совсем ясно в каком бизнесе он далее будет участвовать, там уже на представление дело идет (3), а все бизнес процессы вроде на шаге 1) должны были отработать
Все верно, конвертировать в DTO нужно на уровне сервиса до того, как вернуть данные в контроллер. Пример в статье упрощенный, но не правильный с этой точки зрения.

Аватара пользователя
Roksalana
Сообщения: 213
Зарегистрирован: 2014.01.14, 09:34

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

Сообщение Roksalana » 2017.04.06, 08:01

Faenir писал(а):
2017.04.05, 22:14
Оригинал только у меня не открывается?
Превышено время ожидания ответа от сайта toptal.com.
Интересно было бы увидеть пример реализации чего-то реального на этой архитектуре (на гитхабе). Например, простенького интернет магазина/каталога.
Для начала думаю сделать base app в таком стиле, как будет свободное время. Писать так сложно и становится реально нужно когда есть большая кодовая база и по другому уже будет каша из говнокода. Поэтому никто не пишет так простые проекты/модули, а большие проекты не выкладываются в паблик.

Аватара пользователя
vitovt
Сообщения: 210
Зарегистрирован: 2012.03.21, 10:37
Контактная информация:

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

Сообщение vitovt » 2017.04.06, 08:07

anton_z писал(а):
2017.04.06, 01:36
vitovt, в правильном направлении идете.
Вызывать save у AR по данной методологии нужно в репозитории:

Код: Выделить всё


class OrderRepo 
{
	public function save(Order $order) 
	{
		$order->save(false);
	}
}

Супер! И тут же в репозитории должны быть все действия над объектом? Сделать активным \ неактивным? Записать историю в лог-таблицу к примеру? Все, что касается изменений "заказа" все в репозиторий?

Аватара пользователя
Roksalana
Сообщения: 213
Зарегистрирован: 2014.01.14, 09:34

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

Сообщение Roksalana » 2017.04.06, 08:14

vitovt писал(а):
2017.04.05, 22:53
Вопрос: могу ли я в данном случае репозиторием возвращаться AR в котором будут определены методы getId(), getClientId() и так далее, верно? Пока что, на старте, потом пытаться отойти от этого.
Имхо, вызывать getId() в сервисном слое - плохая идея, т.к по всем сервисам расползутся вызовы AR модели. Если может репозиторий вернуть эти данные - пусть сразу возвращает. Если нет - то инкапсулировать в DTO объект (можно сразу несколько AR моделей передавать) и в нем собирать поля через getId(), getClientId(), сервис будет работать с этим объектом и ему все равно как именно поле расчитано. Завтра clientId будет перемещен (условно) в другую модель - поменяете один метод в нужно DTO и все работает дальше.
vitovt писал(а):
2017.04.05, 22:53
А уже в контрллере я могу сделать что-то вроде

Код: Выделить всё

$order = \Yii::$app->order->getById( 43 );

if( $order->isActive() ) {
  \Yii::$app->order->completeOrder($order);
}
В контроллере вызываем методы AR модели + та самая "бизнес логика" :o Это то от чего нужно уходить. Если от состояния модели зависят дальнейщие действия - это то о чем знает сервис: $order->isActive() => completeOrder - это пример "бизнес логики" в моем понимании. В контроллере получили данные (почистили, преобразовали и тп), передали сервисному слою, получили ответ (почистили, преобразовали и тп если надо) - вернули то что от нас ждут (html, json, xml и тп)

Ответить