Разделение приложения на слои
Разделение приложения на слои
Слушая 12 выпуск пятиминутки php (http://5minphp.ru/episode12/) я нашел интересные мысли от автора RusAlex, "как разграничить сущности в Yii2".
Прочитав тему, я добавил свои мысли и вопросы. Затем подключился разработчик cms yupe и написал свои рекомендации.
В общем я думаю тема интересная и приглашаю подискутировать: https://gist.github.com/RusAlex/0397b42e0f052ec84948
Для развития темы буду стараться добавлять сюда ссылки на другие обсуждения и вопросы касательно разделения приложения на слои.
Статьи по теме:
Yii обмен опытом: модели (окончание) (примеры к yii1, но сути не меняет) - http://habrahabr.ru/post/212917/
Вопросы по теме:
- Куда вынести метод? - viewtopic.php?f=19&t=31240
- Пример организации FormModel - viewtopic.php?f=19&t=32178
Прочитав тему, я добавил свои мысли и вопросы. Затем подключился разработчик cms yupe и написал свои рекомендации.
В общем я думаю тема интересная и приглашаю подискутировать: https://gist.github.com/RusAlex/0397b42e0f052ec84948
Для развития темы буду стараться добавлять сюда ссылки на другие обсуждения и вопросы касательно разделения приложения на слои.
Статьи по теме:
Yii обмен опытом: модели (окончание) (примеры к yii1, но сути не меняет) - http://habrahabr.ru/post/212917/
Вопросы по теме:
- Куда вынести метод? - viewtopic.php?f=19&t=31240
- Пример организации FormModel - viewtopic.php?f=19&t=32178
Последний раз редактировалось nepster 2015.09.25, 11:38, всего редактировалось 5 раз.
Re: Разделение приложения на слои
хорошая тема. Собственно я здесь на форуме (да и не только я) постоянно пишу: используйте сервисы для бизнес-логики. Принцип тонкий контроллер/толстая модель не означает толстую модель, а означает толстый бизнес слой. А бизнес слой это не только модель.
Re: Разделение приложения на слои
Смотрите, я сейчас пишу средний проект (уже около полугода) и в логике практически не выходил за пределы AR. Очень много сущностей, много всяких условий, но в основном все крутится в пределах базы (получить данные и обработать).
Меня больше интересует проблема инкапсуляции выборок в методы, этот вопрос с примером я отписал тут https://gist.github.com/RusAlex/0397b42 ... nt-1511244 ну и собственно пример как, а главное зачем реализовывается паттерн репозиторий yii2 ?
Меня больше интересует проблема инкапсуляции выборок в методы, этот вопрос с примером я отписал тут https://gist.github.com/RusAlex/0397b42 ... nt-1511244 ну и собственно пример как, а главное зачем реализовывается паттерн репозиторий yii2 ?
Re: Разделение приложения на слои
верно. Но это не значит, что вспомогательные методы должны быть в модели.nepster писал(а):Смотрите, я сейчас пишу средний проект (уже около полугода) и в логике практически не выходил за пределы AR. Очень много сущностей, много всяких условий, но в основном все крутится в пределах базы (получить данные и обработать).
Re: Разделение приложения на слои
Ну хорошо, вот например есть 2 метода, где они должны быть:
и
сейчас подобные штуки у меня в моделе.
Код: Выделить всё
public function findById()
{
return self::find()
->where('id' => $id)
->active()
->one();
}
Код: Выделить всё
public function getAddress()
{
return self::country, ' ', self::city,, ' ', self::street;
}
Re: Разделение приложения на слои
в бизнес-слое, но не в модели. В модели вообще ничего не должно быть кроме того, что подразумевается yii-реализацией AR.nepster писал(а):Ну хорошо, вот например есть 2 метода, где они должны быть:
иКод: Выделить всё
public function findById() { return self::find() ->where('id' => $id) ->active() ->one(); }
сейчас подобные штуки у меня в моделе.Код: Выделить всё
public function getAddress() { return self::country, ' ', self::city,, ' ', self::street; }
Первый сниппет - в скоупах (ActiveQuery), второй - это вообще не бизнес, а визуальное представление адреса.
Re: Разделение приложения на слои
А у вас есть где-то пример вашей реализации всех этих подходов "на посмотреть"?zelenin писал(а):в бизнес-слое, но не в модели. В модели вообще ничего не должно быть кроме того, что подразумевается yii-реализацией AR.nepster писал(а):Ну хорошо, вот например есть 2 метода, где они должны быть:
иКод: Выделить всё
public function findById() { return self::find() ->where('id' => $id) ->active() ->one(); }
сейчас подобные штуки у меня в моделе.Код: Выделить всё
public function getAddress() { return self::country, ' ', self::city,, ' ', self::street; }
Первый сниппет - в скоупах (ActiveQuery), второй - это вообще не бизнес, а визуальное представление адреса.
Re: Разделение приложения на слои
так собственно тут такой подход и описан https://gist.github.com/RusAlex/0397b42e0f052ec84948nepster писал(а):А у вас есть где-то пример вашей реализации всех этих подходов "на посмотреть"?zelenin писал(а):в бизнес-слое, но не в модели. В модели вообще ничего не должно быть кроме того, что подразумевается yii-реализацией AR.nepster писал(а):Ну хорошо, вот например есть 2 метода, где они должны быть:
иКод: Выделить всё
public function findById() { return self::find() ->where('id' => $id) ->active() ->one(); }
сейчас подобные штуки у меня в моделе.Код: Выделить всё
public function getAddress() { return self::country, ' ', self::city,, ' ', self::street; }
Первый сниппет - в скоупах (ActiveQuery), второй - это вообще не бизнес, а визуальное представление адреса.
создаются хелперы, они же сервисы, в которые выносится подобный бизнес-функционал.
-
- Сообщения: 957
- Зарегистрирован: 2014.05.31, 20:50
- Откуда: Санкт-Петербург
- Контактная информация:
Re: Разделение приложения на слои
А я сейчас над крупным проектом работаю. Гист пока не читал, но в этом топике отпишусь. Структура, связанная с моделями и БД, на данный момент у меня такая:
app/models/finders - классы, содержащие методы find() от AR
app/models/forms
app/models/queries
app/models/search
app/models/traits
app/sql - чистые SQL запросы
В первую очередь, подобное разделение на TableFinder и TableSql должно помочь при последующей оптимизации. К связям в моделях обращаюсь лишь только, как к геттерам. Разработка на начальном этапе. Возможно, что-то в будущем поправлю или перенесу. Естественно, в моделях находятся только те методы, которые взаимодействуют с AR. Остальное - хелперы или прочие компоненты.
app/models/finders - классы, содержащие методы find() от AR
app/models/forms
app/models/queries
app/models/search
app/models/traits
app/sql - чистые SQL запросы
В первую очередь, подобное разделение на TableFinder и TableSql должно помочь при последующей оптимизации. К связям в моделях обращаюсь лишь только, как к геттерам. Разработка на начальном этапе. Возможно, что-то в будущем поправлю или перенесу. Естественно, в моделях находятся только те методы, которые взаимодействуют с AR. Остальное - хелперы или прочие компоненты.
Re: Разделение приложения на слои
zelenin писал(а):Ну хорошо, вот например есть 2 метода, где они должны быть:
иКод: Выделить всё
public function findById() { return self::find() ->where('id' => $id) ->active() ->one(); }
сейчас подобные штуки у меня в моделе.Код: Выделить всё
public function getAddress() { return self::country, ' ', self::city,, ' ', self::street; }
в бизнес-слое, но не в модели. В модели вообще ничего не должно быть кроме того, что подразумевается yii-реализацией AR.
Первый сниппет - в скоупах (ActiveQuery), второй - это вообще не бизнес, а визуальное представление адреса.
А у вас есть где-то пример вашей реализации всех этих подходов "на посмотреть"?
так собственно тут такой подход и описан https://gist.github.com/RusAlex/0397b42e0f052ec84948
создаются хелперы, они же сервисы, в которые выносится подобный бизнес-функционал.
Да там тупняков много. Пример показывает простейшую реализацию. Что если нужно будет работать одновременно с 3 сущностями ?
Например:
Есть апартамент, у апартамента есть объявления (на час, на сутки, на месяц и тп) и так-же есть картинки + куча разных условий при его создании.
Это нужно на каждую модель плодить сервис, хелперы и еще кучу всего. В результате вернемся туда от куда пришли.
у меня только 2 вопроса.mickgeek писал(а): А я сейчас над крупным проектом работаю. Гист пока не читал, но в этом топике отпишусь. Структура, связанная с моделями и БД, на данный момент у меня такая:
app/models/finders - классы, содержащие методы find() от AR
app/models/forms
app/models/queries
app/models/search
app/models/traits
app/sql - чистые SQL запросы
В первую очередь, подобное разделение на TableFinder и TableSql должно помочь при последующей оптимизации. К связям в моделях обращаюсь лишь только, как к геттерам. Разработка на начальном этапе. Возможно, что-то в будущем поправлю или перенесу. Естественно, в моделях находятся только те методы, которые взаимодействуют с AR. Остальное - хелперы или прочие компоненты.
1) Где модульная архитектура ?
2) как вы реализовываете кучу выборок с разными условиями ?
-
- Сообщения: 957
- Зарегистрирован: 2014.05.31, 20:50
- Откуда: Санкт-Петербург
- Контактная информация:
Re: Разделение приложения на слои
1. Приложение разделено на ядро (основа в app/models, app/components и пр.) и вспомогательные модули (которые могут/будут зависеть от ядра). На данный момент пишется один модуль, который будет пополняться компонентами. В большей степени, он заточен для работы в консольном приложении. Но в будущем планируется утолщать проект и другими модулями. Например, специализированными комментариями.nepster писал(а):у меня только 2 вопроса.
1) Где модульная архитектура ?
2) как вы реализовываете кучу выборок с разными условиями ?
2. Пока таких не было :) Но не вижу трудностей: аргументы в методах + геттеры + скоупы. На выходе можем получить красивый ключ для кэша.
Re: Разделение приложения на слои
у нас есть какой-то интерфейс, в котором мы создаем связь неких сущностей (AppartamentForm). Мы засабмитили форму, отвалидировали, получили на выходе некий набор данных, передали его в AppartamentService::createAppartmentPool(Appartment $appartment, array $images,...). Внутри сделали какую-то логику, что-то с чем-то связали, что-то удалили, что-то сохранили, возможно с помощью других сервисов.nepster писал(а): Да там тупняков много. Пример показывает простейшую реализацию. Что если нужно будет работать одновременно с 3 сущностями ?
Например:
Есть апартамент, у апартамента есть объявления (на час, на сутки, на месяц и тп) и так-же есть картинки + куча разных условий при его создании.
Это нужно на каждую модель плодить сервис, хелперы и еще кучу всего. В результате вернемся туда от куда пришли.
Или так: AppartamentService::createFromAppartmentForm(AppartamentForm $data)
Re: Разделение приложения на слои
zelenin писал(а):у нас есть какой-то интерфейс, в котором мы создаем связь неких сущностей (AppartamentForm). Мы засабмитили форму, отвалидировали, получили на выходе некий набор данных, передали его в AppartamentService::createAppartmentPool(Appartment $appartment, array $images,...). Внутри сделали какую-то логику, что-то с чем-то связали, что-то удалили, что-то сохранили, возможно с помощью других сервисов.nepster писал(а): Да там тупняков много. Пример показывает простейшую реализацию. Что если нужно будет работать одновременно с 3 сущностями ?
Например:
Есть апартамент, у апартамента есть объявления (на час, на сутки, на месяц и тп) и так-же есть картинки + куча разных условий при его создании.
Это нужно на каждую модель плодить сервис, хелперы и еще кучу всего. В результате вернемся туда от куда пришли.
Или так: AppartamentService::createFromAppartmentForm(AppartamentForm $data)
Ну может быть тут AppartamentService более менее оправдан. Но тогда для простых штук, типа добавить комментарий он будет вот такой:
Код: Выделить всё
CommentService::create(Comment $model);
public function create(Comment $model) {
return $model->save();
}
Плюс если вернуться к вашему ответу выше:
Если я вас правильно понял, то:в бизнес-слое, но не в модели. В модели вообще ничего не должно быть кроме того, что подразумевается yii-реализацией AR.
Первый сниппет - в скоупах (ActiveQuery), второй - это вообще не бизнес, а визуальное представление адреса.
1) PostModel - только методы AR и реляции (tableName(), attributeLabels() и тп.)
2) PostQuery - будет содержать не только
Код: Выделить всё
public function active()
{
$this->andWhere([Post::tableName() . '.active' => 1]);
return $this;
}
Код: Выделить всё
public function findById()
{
return Post::find()
->where('id' => $id)
->active()
->one();
}
4) тогда куда все-же девать методы типа ?
Код: Выделить всё
public function getAddress()
{
return self::country, ' ', self::city,, ' ', self::street;
}
Re: Разделение приложения на слои
конечно. Абстракция! Потом у вас будет сложная логика и вы будет выкорчевывать все эти save из десятка мест, вместо того, чтобы в одном методе добавлять логику. А внутри этого метода вы ведь можете легко мигрировать с AR на DAO.nepster писал(а):
Ну может быть тут AppartamentService более менее оправдан. Но тогда для простых штук, типа добавить комментарий он будет вот такой:
Разве тогда есть в нем смысл?Код: Выделить всё
CommentService::create(Comment $model); public function create(Comment $model) { return $model->save(); }
Re: Разделение приложения на слои
вообще файндеры - это удел Репозитория. Но и скоупы - это тоже удел репозитория. Но в Query файндеров быть не должно. Исходя из этого делайте, как считаете правильным.nepster писал(а): Плюс если вернуться к вашему ответу выше:
Если я вас правильно понял, то:в бизнес-слое, но не в модели. В модели вообще ничего не должно быть кроме того, что подразумевается yii-реализацией AR.
Первый сниппет - в скоупах (ActiveQuery), второй - это вообще не бизнес, а визуальное представление адреса.
1) PostModel - только методы AR и реляции (tableName(), attributeLabels() и тп.)
2) PostQuery - будет содержать не толькои так же может содержать методы выборки (или я что-то не то понял) ?Код: Выделить всё
public function active() { $this->andWhere([Post::tableName() . '.active' => 1]); return $this; }
Код: Выделить всё
public function findById() { return Post::find() ->where('id' => $id) ->active() ->one(); }
Re: Разделение приложения на слои
нет, это просто сервисный слой (сервис - обслуживание, обслуживание ваших потребностей в бизнес логике).nepster писал(а):3) PostService - работает с сохранением сущностей.
в слой View ) а как, придумайте сами.nepster писал(а):4) тогда куда все-же девать методы типа ?Код: Выделить всё
public function getAddress() { return self::country, ' ', self::city,, ' ', self::street; }
в чем проблема?nepster писал(а):И как лучше всего оформлять контроллер ?
основная парадигма: приняли данные из request и передали в сервис для обработки данных.
Re: Разделение приложения на слои
А что касается разных условий выборки ?
К примеру в разных частях нашего приложения нам нужны выборки с определенными постами но по разным условиям.
Например:
И мы начинаем плодить 4 очень похожих метода. Как с таким можно бороться ?
Я бы хотел сделать не большой пример на github с реализацией подходов из этой темы, думаю многим будет полезно, если что, сможете потыкать пальцем в сомнительные места ?
К примеру в разных частях нашего приложения нам нужны выборки с определенными постами но по разным условиям.
Например:
Код: Выделить всё
1) нужно найти пост по id со статусом 1
2) нужно найти пост по id со статусом 1 и с реляцией на автора
3) нужно найти пост по слагу со статусом 1, с реляцией на автора и с реляцией на все теги к этому посту
4) нужно найти пост по id со статусом 0, где author_id не равно 1.
Я бы хотел сделать не большой пример на github с реализацией подходов из этой темы, думаю многим будет полезно, если что, сможете потыкать пальцем в сомнительные места ?
Re: Разделение приложения на слои
не вижу проблемы. к тому же вы же можете наплодить скоупов и их юзать в chainnepster писал(а):А что касается разных условий выборки ?
К примеру в разных частях нашего приложения нам нужны выборки с определенными постами но по разным условиям.
Например:И мы начинаем плодить 4 очень похожих метода. Как с таким можно бороться ?Код: Выделить всё
1) нужно найти пост по id со статусом 1 2) нужно найти пост по id со статусом 1 и с реляцией на автора 3) нужно найти пост по слагу со статусом 1, с реляцией на автора и с реляцией на все теги к этому посту 4) нужно найти пост по id со статусом 0, где author_id не равно 1.
не вопросnepster писал(а):Я бы хотел сделать не большой пример на github с реализацией подходов из этой темы, думаю многим будет полезно, если что, сможете потыкать пальцем в сомнительные места ?
Re: Разделение приложения на слои
Давно не было интересных тем на этом форуме, так что если позволите поделюсь мнением и пообщаюсь чуток с вами тут.
Гист прочитал только то что описал автор, тему эту прочитал бегло, ибо много текста.
От себя осмелюсь подправить автора и тех кто называют данный паттерн "Repository", это не так. Это как раз сервисных патерн в своей самой простой реализации с элементами репозитория.
"Repository" патерн подразумевает взаимодействие "Repository" класса с "Persistence" (хранилищем). В идеале он должен использоваться только как мост между "Aplication Layer" и хранилищем. Но не содержать избыточной логики как валидация например, или внешние функции которые используются в контролерах. Это как раз дело сервиса.
Конечно это идеальный вариант и учитывая это можно забить и согласится что описанное является "репозиторием", но наверное это больше похоже на недоделанную комбинацию между Сервисом и Репозиторием, который в идеале должен превратится в "Command" с зависимостью от "Repository".
Уверен что вам надоело моя болтовня, но по теме могу еще добавить следующее:
- При крупных проектах очень удобно разделять приложения на слои
- Лучше всего всегда использовать Repository патерн
- Лучше всего комбинировать это все с "Command" патерном и "CommandBus" классом.
- В идеале использовать для общения между слоями DTO
- Конечно если речь идет о крупных проектах то без Entity и ValueObjects (VO) мы не сможем жить.
- И конечно не забываем про "Identity Map" патерн который обязательно по моему мнению должен присутствовать в "Repository" классе.
- Ну и конечно не забываем про Exception и Marker Interface для наших собственных исключений для хорошего тона.
- Если уже дошли до радикальных мер, то не бойтесь выносить Инфрастуктуру отдельно и используйте Core Domain и чуток вашего кода для хорошего результата.
- Ну и конечно не бойтесь усложнять чуток логику, в будущем это спасет ваш проект. DDD придумали умные люди которые решали сложные задачи, не думайте что эти пару тройку классов и папок усложнят жизнь, они как раз сделают противоположное.
П.С: Почему я это написал? Просто хочется чтобы и на Уии люди писали грамотно, а то фреймворк то вроде хороший, а пишут на нем все как школники.
Гист прочитал только то что описал автор, тему эту прочитал бегло, ибо много текста.
От себя осмелюсь подправить автора и тех кто называют данный паттерн "Repository", это не так. Это как раз сервисных патерн в своей самой простой реализации с элементами репозитория.
"Repository" патерн подразумевает взаимодействие "Repository" класса с "Persistence" (хранилищем). В идеале он должен использоваться только как мост между "Aplication Layer" и хранилищем. Но не содержать избыточной логики как валидация например, или внешние функции которые используются в контролерах. Это как раз дело сервиса.
Конечно это идеальный вариант и учитывая это можно забить и согласится что описанное является "репозиторием", но наверное это больше похоже на недоделанную комбинацию между Сервисом и Репозиторием, который в идеале должен превратится в "Command" с зависимостью от "Repository".
Уверен что вам надоело моя болтовня, но по теме могу еще добавить следующее:
- При крупных проектах очень удобно разделять приложения на слои
- Лучше всего всегда использовать Repository патерн
- Лучше всего комбинировать это все с "Command" патерном и "CommandBus" классом.
- В идеале использовать для общения между слоями DTO
- Конечно если речь идет о крупных проектах то без Entity и ValueObjects (VO) мы не сможем жить.
- И конечно не забываем про "Identity Map" патерн который обязательно по моему мнению должен присутствовать в "Repository" классе.
- Ну и конечно не забываем про Exception и Marker Interface для наших собственных исключений для хорошего тона.
- Если уже дошли до радикальных мер, то не бойтесь выносить Инфрастуктуру отдельно и используйте Core Domain и чуток вашего кода для хорошего результата.
- Ну и конечно не бойтесь усложнять чуток логику, в будущем это спасет ваш проект. DDD придумали умные люди которые решали сложные задачи, не думайте что эти пару тройку классов и папок усложнят жизнь, они как раз сделают противоположное.
П.С: Почему я это написал? Просто хочется чтобы и на Уии люди писали грамотно, а то фреймворк то вроде хороший, а пишут на нем все как школники.
Последний раз редактировалось vova07 2015.07.29, 12:10, всего редактировалось 1 раз.
Re: Разделение приложения на слои
vova07 писал(а): а пишут не нем все как школники.