Разделение приложения на слои

Обсуждаем, как правильно строить приложения
nepster
Сообщения: 838
Зарегистрирован: 2013.01.02, 03:35

Разделение приложения на слои

Сообщение nepster »

Слушая 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
Последний раз редактировалось nepster 2015.09.25, 11:38, всего редактировалось 5 раз.
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Разделение приложения на слои

Сообщение zelenin »

хорошая тема. Собственно я здесь на форуме (да и не только я) постоянно пишу: используйте сервисы для бизнес-логики. Принцип тонкий контроллер/толстая модель не означает толстую модель, а означает толстый бизнес слой. А бизнес слой это не только модель.
nepster
Сообщения: 838
Зарегистрирован: 2013.01.02, 03:35

Re: Разделение приложения на слои

Сообщение nepster »

Смотрите, я сейчас пишу средний проект (уже около полугода) и в логике практически не выходил за пределы AR. Очень много сущностей, много всяких условий, но в основном все крутится в пределах базы (получить данные и обработать).

Меня больше интересует проблема инкапсуляции выборок в методы, этот вопрос с примером я отписал тут https://gist.github.com/RusAlex/0397b42 ... nt-1511244 ну и собственно пример как, а главное зачем реализовывается паттерн репозиторий yii2 ?
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Разделение приложения на слои

Сообщение zelenin »

nepster писал(а):Смотрите, я сейчас пишу средний проект (уже около полугода) и в логике практически не выходил за пределы AR. Очень много сущностей, много всяких условий, но в основном все крутится в пределах базы (получить данные и обработать).
верно. Но это не значит, что вспомогательные методы должны быть в модели.
nepster
Сообщения: 838
Зарегистрирован: 2013.01.02, 03:35

Re: Разделение приложения на слои

Сообщение nepster »

Ну хорошо, вот например есть 2 метода, где они должны быть:

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

public function findById()
{
    return self::find()
        ->where('id' => $id)
        ->active()
        ->one();
}
 
и

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

public function getAddress()
{
    return self::country, '  ', self::city,, '  ', self::street;
}
 
сейчас подобные штуки у меня в моделе.
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Разделение приложения на слои

Сообщение zelenin »

nepster писал(а):Ну хорошо, вот например есть 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), второй - это вообще не бизнес, а визуальное представление адреса.
nepster
Сообщения: 838
Зарегистрирован: 2013.01.02, 03:35

Re: Разделение приложения на слои

Сообщение nepster »

zelenin писал(а):
nepster писал(а):Ну хорошо, вот например есть 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), второй - это вообще не бизнес, а визуальное представление адреса.
А у вас есть где-то пример вашей реализации всех этих подходов "на посмотреть"?
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Разделение приложения на слои

Сообщение zelenin »

nepster писал(а):
zelenin писал(а):
nepster писал(а):Ну хорошо, вот например есть 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
создаются хелперы, они же сервисы, в которые выносится подобный бизнес-функционал.
mickgeek
Сообщения: 957
Зарегистрирован: 2014.05.31, 20:50
Откуда: Санкт-Петербург
Контактная информация:

Re: Разделение приложения на слои

Сообщение mickgeek »

А я сейчас над крупным проектом работаю. Гист пока не читал, но в этом топике отпишусь. Структура, связанная с моделями и БД, на данный момент у меня такая:
app/models/finders - классы, содержащие методы find() от AR
app/models/forms
app/models/queries
app/models/search
app/models/traits
app/sql - чистые SQL запросы

В первую очередь, подобное разделение на TableFinder и TableSql должно помочь при последующей оптимизации. К связям в моделях обращаюсь лишь только, как к геттерам. Разработка на начальном этапе. Возможно, что-то в будущем поправлю или перенесу. Естественно, в моделях находятся только те методы, которые взаимодействуют с AR. Остальное - хелперы или прочие компоненты.
nepster
Сообщения: 838
Зарегистрирован: 2013.01.02, 03:35

Re: Разделение приложения на слои

Сообщение nepster »

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 сущностями ?

Например:
Есть апартамент, у апартамента есть объявления (на час, на сутки, на месяц и тп) и так-же есть картинки + куча разных условий при его создании.

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



mickgeek писал(а): А я сейчас над крупным проектом работаю. Гист пока не читал, но в этом топике отпишусь. Структура, связанная с моделями и БД, на данный момент у меня такая:
app/models/finders - классы, содержащие методы find() от AR
app/models/forms
app/models/queries
app/models/search
app/models/traits
app/sql - чистые SQL запросы

В первую очередь, подобное разделение на TableFinder и TableSql должно помочь при последующей оптимизации. К связям в моделях обращаюсь лишь только, как к геттерам. Разработка на начальном этапе. Возможно, что-то в будущем поправлю или перенесу. Естественно, в моделях находятся только те методы, которые взаимодействуют с AR. Остальное - хелперы или прочие компоненты.
у меня только 2 вопроса.
1) Где модульная архитектура ?
2) как вы реализовываете кучу выборок с разными условиями ?
mickgeek
Сообщения: 957
Зарегистрирован: 2014.05.31, 20:50
Откуда: Санкт-Петербург
Контактная информация:

Re: Разделение приложения на слои

Сообщение mickgeek »

nepster писал(а):у меня только 2 вопроса.
1) Где модульная архитектура ?
2) как вы реализовываете кучу выборок с разными условиями ?
1. Приложение разделено на ядро (основа в app/models, app/components и пр.) и вспомогательные модули (которые могут/будут зависеть от ядра). На данный момент пишется один модуль, который будет пополняться компонентами. В большей степени, он заточен для работы в консольном приложении. Но в будущем планируется утолщать проект и другими модулями. Например, специализированными комментариями.
2. Пока таких не было :) Но не вижу трудностей: аргументы в методах + геттеры + скоупы. На выходе можем получить красивый ключ для кэша.
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Разделение приложения на слои

Сообщение zelenin »

nepster писал(а): Да там тупняков много. Пример показывает простейшую реализацию. Что если нужно будет работать одновременно с 3 сущностями ?

Например:
Есть апартамент, у апартамента есть объявления (на час, на сутки, на месяц и тп) и так-же есть картинки + куча разных условий при его создании.

Это нужно на каждую модель плодить сервис, хелперы и еще кучу всего. В результате вернемся туда от куда пришли.
у нас есть какой-то интерфейс, в котором мы создаем связь неких сущностей (AppartamentForm). Мы засабмитили форму, отвалидировали, получили на выходе некий набор данных, передали его в AppartamentService::createAppartmentPool(Appartment $appartment, array $images,...). Внутри сделали какую-то логику, что-то с чем-то связали, что-то удалили, что-то сохранили, возможно с помощью других сервисов.
Или так: AppartamentService::createFromAppartmentForm(AppartamentForm $data)
nepster
Сообщения: 838
Зарегистрирован: 2013.01.02, 03:35

Re: Разделение приложения на слои

Сообщение nepster »

zelenin писал(а):
nepster писал(а): Да там тупняков много. Пример показывает простейшую реализацию. Что если нужно будет работать одновременно с 3 сущностями ?

Например:
Есть апартамент, у апартамента есть объявления (на час, на сутки, на месяц и тп) и так-же есть картинки + куча разных условий при его создании.

Это нужно на каждую модель плодить сервис, хелперы и еще кучу всего. В результате вернемся туда от куда пришли.
у нас есть какой-то интерфейс, в котором мы создаем связь неких сущностей (AppartamentForm). Мы засабмитили форму, отвалидировали, получили на выходе некий набор данных, передали его в AppartamentService::createAppartmentPool(Appartment $appartment, array $images,...). Внутри сделали какую-то логику, что-то с чем-то связали, что-то удалили, что-то сохранили, возможно с помощью других сервисов.
Или так: 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();
} 
3) PostService - работает с сохранением сущностей.

4) тогда куда все-же девать методы типа ?

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

public function getAddress()
{
    return self::country, '  ', self::city,, '  ', self::street;
} 
И как лучше всего оформлять контроллер ?
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Разделение приложения на слои

Сообщение zelenin »

nepster писал(а):
Ну может быть тут AppartamentService более менее оправдан. Но тогда для простых штук, типа добавить комментарий он будет вот такой:

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

 CommentService::create(Comment $model);
 
 public function create(Comment $model) {
     return $model->save();
 }
Разве тогда есть в нем смысл?
конечно. Абстракция! Потом у вас будет сложная логика и вы будет выкорчевывать все эти save из десятка мест, вместо того, чтобы в одном методе добавлять логику. А внутри этого метода вы ведь можете легко мигрировать с AR на DAO.
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Разделение приложения на слои

Сообщение zelenin »

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();
}
вообще файндеры - это удел Репозитория. Но и скоупы - это тоже удел репозитория. Но в Query файндеров быть не должно. Исходя из этого делайте, как считаете правильным.
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Разделение приложения на слои

Сообщение zelenin »

nepster писал(а):3) PostService - работает с сохранением сущностей.
нет, это просто сервисный слой (сервис - обслуживание, обслуживание ваших потребностей в бизнес логике).
nepster писал(а):4) тогда куда все-же девать методы типа ?

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

public function getAddress()
{
    return self::country, '  ', self::city,, '  ', self::street;
}
в слой View ) а как, придумайте сами.
nepster писал(а):И как лучше всего оформлять контроллер ?
в чем проблема?

основная парадигма: приняли данные из request и передали в сервис для обработки данных.
nepster
Сообщения: 838
Зарегистрирован: 2013.01.02, 03:35

Re: Разделение приложения на слои

Сообщение nepster »

А что касается разных условий выборки ?
К примеру в разных частях нашего приложения нам нужны выборки с определенными постами но по разным условиям.

Например:

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

1) нужно найти пост по id со статусом 1
2) нужно найти пост по id со статусом 1 и с реляцией на автора 
3) нужно найти пост по слагу со статусом 1, с реляцией на автора и с реляцией на все теги к этому посту 
4) нужно найти пост по id со статусом 0, где author_id не равно 1. 
И мы начинаем плодить 4 очень похожих метода. Как с таким можно бороться ?

Я бы хотел сделать не большой пример на github с реализацией подходов из этой темы, думаю многим будет полезно, если что, сможете потыкать пальцем в сомнительные места ?
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Разделение приложения на слои

Сообщение zelenin »

nepster писал(а):А что касается разных условий выборки ?
К примеру в разных частях нашего приложения нам нужны выборки с определенными постами но по разным условиям.

Например:

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

1) нужно найти пост по id со статусом 1
2) нужно найти пост по id со статусом 1 и с реляцией на автора 
3) нужно найти пост по слагу со статусом 1, с реляцией на автора и с реляцией на все теги к этому посту 
4) нужно найти пост по id со статусом 0, где author_id не равно 1. 
И мы начинаем плодить 4 очень похожих метода. Как с таким можно бороться ?
не вижу проблемы. к тому же вы же можете наплодить скоупов и их юзать в chain
nepster писал(а):Я бы хотел сделать не большой пример на github с реализацией подходов из этой темы, думаю многим будет полезно, если что, сможете потыкать пальцем в сомнительные места ?
не вопрос
Аватара пользователя
vova07
Сообщения: 1004
Зарегистрирован: 2012.11.29, 14:52
Откуда: Chisinau, Moldova

Re: Разделение приложения на слои

Сообщение vova07 »

Давно не было интересных тем на этом форуме, так что если позволите поделюсь мнением и пообщаюсь чуток с вами тут.
Гист прочитал только то что описал автор, тему эту прочитал бегло, ибо много текста.

От себя осмелюсь подправить автора и тех кто называют данный паттерн "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 раз.
lynicidn
Сообщения: 2222
Зарегистрирован: 2014.05.24, 15:12

Re: Разделение приложения на слои

Сообщение lynicidn »

vova07 писал(а): а пишут не нем все как школники.
:o
Закрыто