Репозиторий выполняет команды над AR моделью, ему говорят получи данные или сохрани - он выполняет. Но он не знает зачем он это делал. Знает только сервис. Сервис решает когда сделать модель не активной и дает команду репозиторию. Репозиторий выполняет указание сервиса. Сервис решает что нужно залогировать в лог-таблицу заказ, создать новую запись, поменять что-то в текущей и тп. Но он начальник, а всю черную работу будет делать репозиторий.
Слоистая архитектура для Yii приложений
Re: Слоистая архитектура для Yii приложений
Re: Слоистая архитектура для Yii приложений
Имхо, вызывать getId() в сервисном слое - плохая идеяRoksalana писал(а): ↑2017.04.06, 08:14Имхо, вызывать 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); }
Тут я имел в виду, что репозиторий, чтобы получить, например, ID заказа, может дернуть $model->getId() а будет реализован этот метод в классе, наследуемом от AR или какой-то чистый DTO - уже вторично?
И второе, получается, что в контроллере я должен сделать что-то простое, типа
Код: Выделить всё
try {
\Yii::$app->order->completeOrderById( 47 );
}catch(\Exception $e) {
...
}
Код: Выделить всё
public function completeOrderById(int $id) {
$model = $this->findOne( $id );
if( $model->isActive() ) {
$this->completeOrder( $model );
}
}
public function findOne( $id ) {
return $this->orderRepository->findOne( $id );
}
public function completeOrder( $model ) {
$this->orderRepository->updateAll(
...
);
}
И еще вопрос транзакицй. Предпочитаю многие запросы оборачивать транзакциями, куда их в данном случае помещать (старт транзакции, роллбэк), получается в репозиторий? Но репозиторий делает простые команды, а транзакции отслеживают сразу 2-3 операции над базой. Тогда по логике транзакцию надо стартовать и коммитить в сервисном слое, но он же не должен знать откуда данные поступают к нему?
Последний раз редактировалось vitovt 2017.04.06, 09:25, всего редактировалось 2 раза.
Re: Слоистая архитектура для Yii приложений
Получается, что репозиторий - это класс, который по сути умеет create, read, update, delete таким способом каким ему разрешено (файл, база, activerecord и т.д.)Roksalana писал(а): ↑2017.04.06, 08:18Репозиторий выполняет команды над AR моделью, ему говорят получи данные или сохрани - он выполняет. Но он не знает зачем он это делал. Знает только сервис. Сервис решает когда сделать модель не активной и дает команду репозиторию. Репозиторий выполняет указание сервиса. Сервис решает что нужно залогировать в лог-таблицу заказ, создать новую запись, поменять что-то в текущей и тп. Но он начальник, а всю черную работу будет делать репозиторий.
Сервис делает все - вызывает объект, проверяет его, делает над ним какую-то работу (ставит активнм неактивным через команды репозиторию). Если раньше я делал так
Код: Выделить всё
Order extends ActiveRecord {
public function setActive() {
self::updateAll(['active'=>1], "id='" . $this->id . "'");
}
}
Re: Слоистая архитектура для Yii приложений
Репозиторий может дернуть $model->getId(), а сервис не должен ничего дергать у AR модели.
Транзакции должны быть на уровне сервиса, так как это тоже часть "логики" (откатить в случаи неудачи на каком-то этапе). Но опять таки - сервис не начинает их сам и не заканчивает, а делает это через репозиторий (через вызовы методов). Тогда можно поменять схему хранения данных, не меняя логики (репозиторий будет реализовывать транзакции, к примеру сам в ручную запоминать операции для отката, если к примеру данные хранятся в файлах и там нет вообще системы транзакций).
Транзакции должны быть на уровне сервиса, так как это тоже часть "логики" (откатить в случаи неудачи на каком-то этапе). Но опять таки - сервис не начинает их сам и не заканчивает, а делает это через репозиторий (через вызовы методов). Тогда можно поменять схему хранения данных, не меняя логики (репозиторий будет реализовывать транзакции, к примеру сам в ручную запоминать операции для отката, если к примеру данные хранятся в файлах и там нет вообще системы транзакций).
Re: Слоистая архитектура для Yii приложений
Тут репозиторий это не совсем репозиторий. Правильнее назвать его TableGateway. Через него будут выполняться все запросы к таблице, запись в которой представляет AR. Соответственно, сохранение связанных записей вместе с транзакциями ложится на сервис. В сервисах транзакцию стартуете, сохраняете записи (AR) в соответствующие таблицы, коммит. Тут конечно не DDD, но зависимости мокаются и с распределением обязанностей лучше.
Roksalana, правильные ли я сделал замечания? Или у вас репозиторий умеет связи сохранять?
Roksalana, правильные ли я сделал замечания? Или у вас репозиторий умеет связи сохранять?
Re: Слоистая архитектура для Yii приложений
Order может содержать такие методы, но экземпляр класса Order не должен "знать" о них = не должен вызывать такие методы сам у себя (кроме каких-то private методов, но это отдельная история). Такие методы, так же как и отношения моделей можно и нужно использовать, но вызывать их на уровне репозитория. Сам отдельный order - просто класс данных без методов (кроме геттеров).vitovt писал(а): ↑2017.04.06, 09:21то теперь это делает сервисный слой, через команду репозиторию? В таком случае у меня Order содержит только геттеры для доступа к данным (мы пока не говорим как они туда попадают, в случае с ActiveRecord - из базы при инициации, в случае DTO - через конструктор как я понимаю программист сам туда их загоянет).Код: Выделить всё
Order extends ActiveRecord { public function setActive() { self::updateAll(['active'=>1], "id='" . $this->id . "'"); } }
Re: Слоистая архитектура для Yii приложений
Да, тут репозиторий это не чистый репозиторий в терминах DDD Вообще это не структура DDD, зато более понятная и применимая для средне-крупных проектов. Сервис оперирует данными, конткретное хранение - дело репозитория, так что по поводу сохранения связей, имхо все зависит от задачи. Сервис не знает какие именно таблицы существует и не просит сохранить данные в таблицу A и таблицу B (так что тут не чистый TableGateway тоже), он просит "сохранить заказ", а куда - забота репозитория, но если нужно сохранить заказ, добавить задачу на упаковку и доставку, уведомить юзера об успешном заказе - то это уже будет делать сервис (раздавать указания в каком порядке кому что делать).anton_z писал(а): ↑2017.04.06, 10:08 Тут репозиторий это не совсем репозиторий. Правильнее назвать его TableGateway. Через него будут выполняться все запросы к таблице, запись в которой представляет AR. Соответственно, сохранение связанных записей вместе с транзакциями ложится на сервис. В сервисах транзакцию стартуете, сохраняете записи (AR) в соответствующие таблицы, коммит. Тут конечно не DDD, но зависимости мокаются и с распределением обязанностей лучше.
Roksalana, правильные ли я сделал замечания? Или у вас репозиторий умеет связи сохранять?
Re: Слоистая архитектура для Yii приложений
Ну тут тогда проблема с сохранением связей тогда. Допустим у вас заказ с 100 позициями. Заказ был отредактирован - у него сняли четыре позиции, две добавили. При таком подходе, без UoW и проксирования при сохранении заказа придется вытянуть все позиции а затем пересохранить их.
С TableGateway тут лучше.
С TableGateway тут лучше.
Последний раз редактировалось anton_z 2017.04.06, 10:49, всего редактировалось 2 раза.
Re: Слоистая архитектура для Yii приложений
Ага, получается, главная работа в сервисном слое, а репозиторий - некая прослойка, в данном случае для подключения к AR или базе напрямую.Roksalana писал(а): ↑2017.04.06, 10:03 Репозиторий может дернуть $model->getId(), а сервис не должен ничего дергать у AR модели.
Транзакции должны быть на уровне сервиса, так как это тоже часть "логики" (откатить в случаи неудачи на каком-то этапе). Но опять таки - сервис не начинает их сам и не заканчивает, а делает это через репозиторий (через вызовы методов). Тогда можно поменять схему хранения данных, не меняя логики (репозиторий будет реализовывать транзакции, к примеру сам в ручную запоминать операции для отката, если к примеру данные хранятся в файлах и там нет вообще системы транзакций).
Сервисный слой говорит: "- стартани мне транзакцию!", а в репозитории уже своя реализация этого метода (или чистый SQL или \Yii::$app->db->beginTransaction();)
Остальные методы вызываются также. Нужно что-то выбрать? Вызываем метод репозитория который возвращает некий объект или коллекцию объектов. В сервисе мы всегда знаем какого типа этот объект, какие у него есть методы.
Вот тут у меня и возник вопрос с getId()
Если я в сервисе делаю
Код: Выделить всё
$model = $this->orderRepository->findOne($id);
Тогда в случае смены хранилища, я меняю репозиторий, который, например, работает с MongoDB и он все равно возвращает мне модель у которой есть getId() просто теперь это не объект отнаследованный от AR а просто класс class Order { } в который я на уровне репозитория загнал нужные данные.
Например,
Код: Выделить всё
new Order($row['id'], DateTime($row['created_at']) .... );
Re: Слоистая архитектура для Yii приложений
Про смену хранилища лучше не думайте - это бывает крайне редко и все равно требует огромного рефакторинга из-за рассогласования нагрузки.
Транзакции надо стартовать и коммитить с помощью отдельного сервиса (TransactionManager. какой-нибудь), потому что могут быть транзации на несколько репозиториев.
Транзакции надо стартовать и коммитить с помощью отдельного сервиса (TransactionManager. какой-нибудь), потому что могут быть транзации на несколько репозиториев.
Re: Слоистая архитектура для Yii приложений
Да, точно!anton_z писал(а): ↑2017.04.06, 10:29 Про смену хранилища лучше не думайте - это бывает крайне редко и все равно требует огромного рефакторинга из-за рассогласования нагрузки.
Транзакции надо стартовать и коммитить с помощью отдельного сервиса (TransactionManager. какой-нибудь), потому что могут быть транзации на несколько репозиториев.
- slavcodev
- Сообщения: 3134
- Зарегистрирован: 2009.04.02, 21:42
- Откуда: Valencia
- Контактная информация:
Re: Слоистая архитектура для Yii приложений
Добавлю свои пять копеек
1) DTO - объект данных передающийся между слоями. Создавать его в контролере, в Presentation Layer (т.е. самом крайнем) безполезно. Скорее всего это ДТО должен возвращаться из сервисов.
2) Декораторы над DTO? Для чего это может быть полезно?
3) Generic DTO + костыльные декораторы? Для чего эти сложности? Для "архитектуры"? Специфичные DTO сделают код лучше.
4) DTO::make, Decorator::decorate() - чем отличаются/лучше использования конструктора? Мода на статические метода - фабрики?
ЗЫ: +1 к тому что слишком много магии в коде, и UserRepository - это не понятно что.
1) DTO - объект данных передающийся между слоями. Создавать его в контролере, в Presentation Layer (т.е. самом крайнем) безполезно. Скорее всего это ДТО должен возвращаться из сервисов.
2) Декораторы над DTO? Для чего это может быть полезно?
3) Generic DTO + костыльные декораторы? Для чего эти сложности? Для "архитектуры"? Специфичные DTO сделают код лучше.
4) DTO::make, Decorator::decorate() - чем отличаются/лучше использования конструктора? Мода на статические метода - фабрики?
ЗЫ: +1 к тому что слишком много магии в коде, и UserRepository - это не понятно что.
Жду Yii 3!
Re: Слоистая архитектура для Yii приложений
А что предлагаете вместов него? AR? Форму с горой зависимостей в сервис отдавать?
Профит такой, что dto хорошо сериализуются, легко перейти к шине команд в случае чего или залоггировать.
Еще dto зависит от контекста. Если операция меняет 10 свойств у сущности, которая имеет 100 свойств, лучше давать доступ только к тому, что нужно для выполнения операции
Для форматирования, чтобы в шаблонах всякие форматтеры не вызывать, не писать горы кода по преобразованию в нужный формат, а все сделать в специально предназначенном для этого классе
Ответ на п.2 здесь подойдет.
Это паттерн named constructor. Позволяет сделать несколько конструкторов. В PHP нет перегрузки функций.
Где тут магия? Ни __get() ни __invoke(), ничего такого нет. Просто более грамотно распределили обязанности. Классов стало больше. Но они простые. С AR сняли обязанность по сохранению самой себя в БД, передав ее сервису.
Да, тут есть вопросы по обязанностям данных "репозиториев" - должны ли они сохранять одну запись или несколько. Если несколько - могут проблемы с реализацией возникнуть. Я склонен считать данные "репозитории" более близкими к TableGateway, почти забытому ныне паттерну.
Данный подход куда лучше, чем делать "как придется", как делает большинство.
Мне вот очень зендовский подход нравится - просто и тестировать можно, ну и плевать, что есть exchangeArray():
https://docs.zendframework.com/tutorial ... nd-models/
Re: Слоистая архитектура для Yii приложений
Вот кстати любопытная ссылка попалась чтобы понимать как проектировать тот или иной слой
http://stackoverflow.com/questions/1617 ... ign-in-php
Но в этом примере контроллер работает сразу через репозиторий, что, как я понимаю, не всегда удобно? Или так можно было? =)
Тогда в сервисном слое получается бОльшая часть методово совпадает с методами в репозитории.
http://stackoverflow.com/questions/1617 ... ign-in-php
Но в этом примере контроллер работает сразу через репозиторий, что, как я понимаю, не всегда удобно? Или так можно было? =)
Тогда в сервисном слое получается бОльшая часть методово совпадает с методами в репозитории.
Последний раз редактировалось vitovt 2017.04.07, 07:35, всего редактировалось 1 раз.
Re: Слоистая архитектура для Yii приложений
Я думаю, сложно начать применять слоистую архитектуру, по крайней мере для меня, становится тот факт, что работая с Yii очень легко поддаться на удобство, когда ты в контроллере делаешь что-то вроде
и реализуешь в методе search() стандартный dataProvider
и это легко и просто, а вот когда хочешь перейти на сервисы, репозитории, сущности, возникает вопрос как это все дело менять, пейджинги, филтрации, вот это все.
(полагаю, что также но внутри есть ощущение что что-то не так)
Но для стандартных вещей, конечно легко -
или там
Вот эти вещи как раз из статьи и непонятны. И понятно дело почему все ищут какой-то хороший пример: чтобы как раз подсмотреть реализацию нестандартных вещей.
Код: Выделить всё
$dataProvider = (new OrderSearch())->search($params);
и это легко и просто, а вот когда хочешь перейти на сервисы, репозитории, сущности, возникает вопрос как это все дело менять, пейджинги, филтрации, вот это все.
(полагаю, что также но внутри есть ощущение что что-то не так)
Но для стандартных вещей, конечно легко -
Код: Выделить всё
\Yii::$app->order->findOne(123);
Код: Выделить всё
\Yii::$app->order->getAllItemsByOrderId(123);
Re: Слоистая архитектура для Yii приложений
Если order - это у вас сервис, непонятно почему вы его используете как yii компонент. В статье ни о чем таком не говорилось.vitovt писал(а): ↑2017.04.07, 07:29 Но для стандартных вещей, конечно легко -Код: Выделить всё
\Yii::$app->order->findOne(123);
Не вижу где в статье инфа о сохранении AR из сервиса, и что конкретно это значит? Как то не через $ArModel->save() ?
Re: Слоистая архитектура для Yii приложений
В статье нет прям такого примера. Репозиторий сохраняет объекты по определению. Иначе он не репозиторий вообще.
Re: Слоистая архитектура для Yii приложений
Так можно сказать сложилось исторически, приходится с этим работать да и потом, работая с Yii пока что так удобнее. Вот уже сам компонент (он как бы прослойка между Yii и всеми остальными слоями).maleks писал(а): ↑2017.04.07, 08:14Если order - это у вас сервис, непонятно почему вы его используете как yii компонент. В статье ни о чем таком не говорилось.vitovt писал(а): ↑2017.04.07, 07:29 Но для стандартных вещей, конечно легко -Код: Выделить всё
\Yii::$app->order->findOne(123);
Если не правильно то как надо тогда?
Как я писал выше кто-то сразу в контроллере лезет в репозиторий
Код: Выделить всё
class SiteController extends Controller {
public function __construct(OrderRepository $repository) {
$this->repository = $repository;
}
}
Re: Слоистая архитектура для Yii приложений
На каком-то форуме услышал мнение, что репозиторий - это только отображение коллекции. Тогда вообще стало не понятно, а где же сохранять сущность свою. Но здесь мнение о том что репозиторий должен сохранять как-то даже логично звучит.
Re: Слоистая архитектура для Yii приложений
так интерфейс должен быть прописан, а не реализация - тогда менять ничего не придется.vitovt писал(а): ↑2017.04.07, 09:21Как я писал выше кто-то сразу в контроллере лезет в репозиторий
но тогда в случае смены репозитория надо везде во всех контроллерах менять код, а так только в одном сервисном слое. Нет? Да и в конторллере может понадобится не 1 репозитоий а 2 -5 (заказы, товары в заказе, клиенты и т.д.).Код: Выделить всё
class SiteController extends Controller { public function __construct(OrderRepository $repository) { $this->repository = $repository; } }