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

Обсуждаем, как правильно строить приложения
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

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

Сообщение zelenin » 2017.04.07, 09:24

vitovt писал(а):
2017.04.07, 09:23
anton_z писал(а):
2017.04.07, 09:18
В статье нет прям такого примера. Репозиторий сохраняет объекты по определению. Иначе он не репозиторий вообще.
На каком-то форуме услышал мнение, что репозиторий - это только отображение коллекции. Тогда вообще стало не понятно, а где же сохранять сущность свою. Но здесь мнение о том что репозиторий должен сохранять как-то даже логично звучит.
ну так из коллекции мы можем доставать и можем в коллекцию добавлять.

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

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

Сообщение vitovt » 2017.04.07, 09:42

zelenin писал(а):
2017.04.07, 09:23
vitovt писал(а):
2017.04.07, 09:21
Как я писал выше кто-то сразу в контроллере лезет в репозиторий

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

class SiteController extends Controller {
	public function __construct(OrderRepository $repository) {
		$this->repository = $repository;
	}
}
но тогда в случае смены репозитория надо везде во всех контроллерах менять код, а так только в одном сервисном слое. Нет? Да и в конторллере может понадобится не 1 репозитоий а 2 -5 (заказы, товары в заказе, клиенты и т.д.).
так интерфейс должен быть прописан, а не реализация - тогда менять ничего не придется.
Не совсем понял, можете поподробнее?

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

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

Сообщение zelenin » 2017.04.07, 09:45

public function __construct(OrderRepositoryInterface $repository) {

интерфейс-то у нас не меняется, поэтому меняя реализации, у код менять не нужно.

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

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

Сообщение vitovt » 2017.04.07, 10:30

zelenin писал(а):
2017.04.07, 09:45
public function __construct(OrderRepositoryInterface $repository) {

интерфейс-то у нас не меняется, поэтому меняя реализации, у код менять не нужно.
А в случае когда надо 2-5 репозиториев, что все загонять в конструктор? Как я писал выше, например, надо в контроллере вывести заказ #5

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

$this->orderRepository->findOne(5);
а теперь тут же посчитать количество товары или вывести все товары в заказе?

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

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

Сообщение zelenin » 2017.04.07, 10:35

vitovt писал(а):
2017.04.07, 10:30
zelenin писал(а):
2017.04.07, 09:45
public function __construct(OrderRepositoryInterface $repository) {

интерфейс-то у нас не меняется, поэтому меняя реализации, у код менять не нужно.
А в случае когда надо 2-5 репозиториев, что все загонять в конструктор? Как я писал выше, например, надо в контроллере вывести заказ #5

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

$this->orderRepository->findOne(5);
а теперь тут же посчитать количество товары или вывести все товары в заказе?
вопрос в чем? 5 репозиториев - 5 интерфейсов. В чем вопрос?

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

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

Сообщение anton_z » 2017.04.07, 10:38

Если у вас конструктор контроллера требует больше 3-4 сервисов - ваш контроллер имеет слишком много обязанностей. Надо разделять по разным контроллерам. Ну либо внедрение метода (чего в yii вроде как нет) по экшенам.

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

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

Сообщение vitovt » 2017.04.07, 14:53

anton_z писал(а):
2017.04.07, 10:38
Если у вас конструктор контроллера требует больше 3-4 сервисов - ваш контроллер имеет слишком много обязанностей. Надо разделять по разным контроллерам. Ну либо внедрение метода (чего в yii вроде как нет) по экшенам.
В вопросе разделения на слои можно дойти до безумства. Смотря на пример который мы тут рассматриваем - заказы легко запутаться и пойти не в ту сторону.

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

репозиторий Заказы возвращает объект Заказа, который в себе должен \ может содержать информацию только о себе, верно? Значит чтобы получить и заказ и список товаров или хотя бы количество товаров в заказе - нужно второй репозиторий задействовать и делать так:

1. Выбрал заказ
2. Выбрал товары по заказу
3. Выбрал клиента по заказу

Вот с этим получается путаница, потому, как в AR в частности в YII очень легко поддаться магии в виде Relations и возвращения каких-то связанных данных из других таблиц используя геттеры. (Например выбрал Заказ, у него есть метод getItems() который отдает заказы, есть метод hasItems() который дергает countItems() который смотрит количество заказов и т.д.

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

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

Сообщение ElisDN » 2017.04.07, 15:29

vitovt писал(а):
2017.04.07, 14:53
репозиторий Заказы возвращает объект Заказа, который в себе должен \ может содержать информацию только о себе, верно? Значит чтобы получить и заказ и список товаров или хотя бы количество товаров в заказе - нужно второй репозиторий задействовать и делать так...
Для листингов на сайте делают ReadModel и ReadRepository как здесь и здесь. И там уже организуют любые нужные JOIN-ы.

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

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

Сообщение anton_z » 2017.04.08, 02:41

Попробовал предложенный способ в качестве рефакторинга проекта на Yii1. Толку мало. Тестировать сервисы модульными тестами без БД не получается, так как AR в Yii1 при конструировании лезет в БД за схемой! В Yii2 пока не пробовал, посмотрим что там.

UPD. Беглый просмотр кода yii\db\ActiveRecord показал, что присваивание любых свойств которые хранятся в _attributes приведет к вызову метода attributes(), который также полезет за схемой.

В общем, при проверке предложенный способ лишился своей главной прелести - чистого модульного тестирования сервисов, что, на мой вгляд, сильно уменьшило его полезность.

P.S. Если хотите модульное тестирование - забудьте про Yii AR.


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

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

Сообщение anton_z » 2017.04.09, 06:39

Спасибо за ссылку, но это не сильно поможет. Например ProductCreator - он ведь создает оператором new AR Product. Можно конечно в фабрику вынести, но это еще большие костыли.

Аватара пользователя
slavcodev
Сообщения: 3133
Зарегистрирован: 2009.04.02, 21:42
Откуда: Altea, Spain
Контактная информация:

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

Сообщение slavcodev » 2017.04.11, 04:29

anton_z писал(а):
2017.04.06, 15:40
slavcodev писал(а):
2017.04.06, 14:00
1) DTO - объект данных передающийся между слоями. Создавать его в контролере, в Presentation Layer (т.е. самом крайнем) безполезно. Скорее всего это ДТО должен возвращаться из сервисов.
А что предлагаете вместов него? AR? Форму с горой зависимостей в сервис отдавать?
Я ничего не говорил о том что в сервис отдавать, я написал про то что сервис возвращает.
anton_z писал(а):
2017.04.06, 15:40
Для форматирования, чтобы в шаблонах всякие форматтеры не вызывать, не писать горы кода по преобразованию в нужный формат, а все сделать в специально предназначенном для этого классе
Форматирование это дело шаблонов, особенно если юзается шаблонизатор, это уже куча классов обрабатывающих данные, зачем тут еще какие-то декораторы? Если у нас DTO - User, и на двух страницах надо вывести дату регистрации в разном формате, надо два декоратора?
Кому нужны такие сложности?
anton_z писал(а):
2017.04.06, 15:40
Это паттерн named constructor. Позволяет сделать несколько конструкторов. В PHP нет перегрузки функций.
Ок. Я в курсе про это паттерн. Но как и все паттерны надо использовать там где они не обходимы а не лишь бы юзать.
Паттерны создания объектов нужны для сложных инициализаций а не для обертки на конструктором.
anton_z писал(а):
2017.04.06, 15:40
Где тут магия? Ни __get() ни __invoke(), ничего такого нет.
Не знаю наверное мы разные статьи читали, с разным кодом. То что я видел там везде __get, __call, из-за генериков.
anton_z писал(а):
2017.04.07, 10:38
Если у вас конструктор контроллера требует больше 3-4 сервисов - ваш контроллер имеет слишком много обязанностей. Надо разделять по разным контроллерам. Ну либо внедрение метода (чего в yii вроде как нет) по экшенам.
Это какие такие "много обязанностей"? Я вижу только одну, обработать запрос, делегировать обработку в сервис и подготовить ответ.
Жду Yii 3!

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

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

Сообщение anton_z » 2017.04.11, 07:21

slavcodev писал(а):
2017.04.11, 04:29
Форматирование это дело шаблонов, особенно если юзается шаблонизатор, это уже куча классов обрабатывающих данные, зачем тут еще какие-то декораторы? Если у нас DTO - User, и на двух страницах надо вывести дату регистрации в разном формате, надо два декоратора?
Кому нужны такие сложности?
Создать два простых класса это ой как сложно, прям капец)

Тут все дело в зависимостях. Отдельные DTO для шаблонов нужны для того, чтобы снизить зависимость шаблонов от домена. (VIEW от MODEL в MVC) Используя отдельный DTO, будет очень легко заменить User на какую-то другую DTO или сущность, два, три других класса, не трогая код шаблона, который зависит только от DTO.
anton_z писал(а):
2017.04.06, 15:40
Ок. Я в курсе про это паттерн. Но как и все паттерны надо использовать там где они не обходимы а не лишь бы юзать.
Паттерны создания объектов нужны для сложных инициализаций а не для обертки на конструктором.
Тут дело вкуса. Named constructor очень прост и удобен, у него нет недостатков. Он не предназначен для сложной инициализации. Это просто конструктор и именем и все.

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

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

Сообщение Roksalana » 2017.04.11, 11:28

vitovt писал(а):
2017.04.07, 14:53
Например, как я писал выше, у заказа есть товары, клиент

репозиторий Заказы возвращает объект Заказа, который в себе должен \ может содержать информацию только о себе, верно? Значит чтобы получить и заказ и список товаров или хотя бы количество товаров в заказе - нужно второй репозиторий задействовать и делать так:

1. Выбрал заказ
2. Выбрал товары по заказу
3. Выбрал клиента по заказу

Вот с этим получается путаница, потому, как в AR в частности в YII очень легко поддаться магии в виде Relations и возвращения каких-то связанных данных из других таблиц используя геттеры. (Например выбрал Заказ, у него есть метод getItems() который отдает заказы, есть метод hasItems() который дергает countItems() который смотрит количество заказов и т.д.
Я бы делала все в методах одного репозитория: getOrder(), getOrderWithItems() и тп. И в репозитории уже скрыто, как мы получаем item к заказу, через отношения модели или вывозвом другого приватного метода с отдельным запросом. Т.к по сути это все заказ. Я "за" разумное использование отношений и AR-магии. Если это разные репозитории, то сервис должен думать о том, что заказ это один репозиторий, его товары это другой. А это мелочь, не вопросы уровня сервиса.

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

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

Сообщение vitovt » 2017.04.11, 12:02

Roksalana писал(а):
2017.04.11, 11:28
vitovt писал(а):
2017.04.07, 14:53
Например, как я писал выше, у заказа есть товары, клиент

репозиторий Заказы возвращает объект Заказа, который в себе должен \ может содержать информацию только о себе, верно? Значит чтобы получить и заказ и список товаров или хотя бы количество товаров в заказе - нужно второй репозиторий задействовать и делать так:

1. Выбрал заказ
2. Выбрал товары по заказу
3. Выбрал клиента по заказу

Вот с этим получается путаница, потому, как в AR в частности в YII очень легко поддаться магии в виде Relations и возвращения каких-то связанных данных из других таблиц используя геттеры. (Например выбрал Заказ, у него есть метод getItems() который отдает заказы, есть метод hasItems() который дергает countItems() который смотрит количество заказов и т.д.
Я бы делала все в методах одного репозитория: getOrder(), getOrderWithItems() и тп. И в репозитории уже скрыто, как мы получаем item к заказу, через отношения модели или вывозвом другого приватного метода с отдельным запросом. Т.к по сути это все заказ. Я "за" разумное использование отношений и AR-магии. Если это разные репозитории, то сервис должен думать о том, что заказ это один репозиторий, его товары это другой. А это мелочь, не вопросы уровня сервиса.

Меня наверное смутила фраза, сказанная где-то тут или в соседней ветке, что типа одна реализация не должна знать ничего про другую. Вот я и растолковал, что грубо говоря на каждый объект DTO свой репозиторий который не знает про существование другого.

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

Аватара пользователя
slavcodev
Сообщения: 3133
Зарегистрирован: 2009.04.02, 21:42
Откуда: Altea, Spain
Контактная информация:

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

Сообщение slavcodev » 2017.04.11, 12:56

anton_z писал(а):
2017.04.11, 07:21
Создать два простых класса это ой как сложно, прям капец)

Тут все дело в зависимостях. Отдельные DTO для шаблонов нужны для того, чтобы снизить зависимость шаблонов от домена. (VIEW от MODEL в MVC) Используя отдельный DTO, будет очень легко заменить User на какую-то другую DTO или сущность, два, три других класса, не трогая код шаблона, который зависит только от DTO.
Смешались в кучу кони люди.

DTO нужны не для того чтоб шаблоны (каждый отдельно шаблон отделить от домена), а для того чтоб отделить два слоя. DTO может быть один на сущность, один ДТО представлять данные больше одной сущности, или может быть несколько ДТО на одну сущность. Правил тут нет, есть задача. Но вот только, делать отдельный ДТО для каждого шаблона, это перебор. Я бы сказал даже что мусор в приложении.

Сложность тут не в количестве классов, дело в поддержке.

- Новичкам въехать и запомнить каждый класс + к проблемам.
- В больших командах и большим приложением, не уследишь за изменениями а реакция на изменения в индустрии должны отражаться в приложении мгновенно, не когда разбираться и кучи лишних "простых классах"
Жду Yii 3!

Аватара пользователя
rugabarbo
Сообщения: 1061
Зарегистрирован: 2015.06.21, 16:21
Контактная информация:

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

Сообщение rugabarbo » 2017.04.11, 13:20

DTO - это просто способ передавать в упакованном виде набор аргументов. DTO - это не полноценный класс/объект, а что-то вроде структур в Си (все свойства публичные, поведения нет). Если бы PHP поддерживал именованные параметры, то и без DTO можно было бы обойтись. Костыльная замена DTO в PHP – симфонийский OptionsResolver, но для него нет автокомплита в IDE.

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

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

Сообщение zelenin » 2017.04.11, 13:25

rugabarbo писал(а):
2017.04.11, 13:20
Если бы PHP поддерживал именованные параметры, то и без DTO можно было бы обойтись
каким образом?
rugabarbo писал(а):
2017.04.11, 13:20
Костыльная замена DTO в PHP – симфонийский OptionsResolver
какая связь optionsresolver и dto?

Аватара пользователя
rugabarbo
Сообщения: 1061
Зарегистрирован: 2015.06.21, 16:21
Контактная информация:

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

Сообщение rugabarbo » 2017.04.11, 13:36

zelenin писал(а):
2017.04.11, 13:25
rugabarbo писал(а):
2017.04.11, 13:20
Если бы PHP поддерживал именованные параметры, то и без DTO можно было бы обойтись
каким образом?
rugabarbo писал(а):
2017.04.11, 13:20
Костыльная замена DTO в PHP – симфонийский OptionsResolver
какая связь optionsresolver и dto?
В случае именованных параметров (как в Objective-C, например), ты бы кидал в функцию что-то вроде:

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

$service->doTask(:name => $nameVar, :date => $date);
Добавился, например, какой-нибудь статус, ты бы его добавил:

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

$service->doTask(:name => $nameVar, :date => $date, :status => $status);
Нужно убрать дату - убрал, не заботясь о порядке аргументов:

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

$service->doTask(:name => $nameVar, :status => $status);
И не приходилось бы каждый раз заботиться о инициализации DTO:

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

$dto = new NewTaskDTO;
$dto->name = $nameVar;
$dto->status = $status;

$service->doTask($dto);
DTO - он же даёт тебе просто свободу от жёсткой сигнатуры функций. Это просто способ передавать аргументы из слоя в слой.

Аватара пользователя
rugabarbo
Сообщения: 1061
Зарегистрирован: 2015.06.21, 16:21
Контактная информация:

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

Сообщение rugabarbo » 2017.04.11, 13:37

Ну а OptionsResolver как раз эмулирует именованные параметры в пыхе. Отсюда и связь со всем вышеуказанным.

Ответить