\m/^^zelenin писал(а):http://habrahabr.ru/post/267125/
отличный перевод обширной статьи о hexagonal архитектуре
Разделение приложения на слои
Re: Разделение приложения на слои
Очень плохие и совсем бесполезные расширения: http://github.com/bupy7
Re: Разделение приложения на слои
Фух! Осилил таки! ))
Пописал с 2007 года проект для своей конторы самописом, но в конце концов все стало очень сложно в поддержке. Прикольно конечно, когда полностью свободен от всего, но потом начинает сильно доставать, что либо одного не хватает, либо другого, а все полностью переписывать уже сложновато. Проще фреймворком воспользоваться, где многие вещи уже сделаны грамотно и многими людьми выверены.))S c писал(а):А я с вами соглашусь. Сам планирую для себя реализовать средненький проект без использования фреймворка. Добавлю сюда, что это незаменимый опыт. Именно на таких проектах и рождаются первоклассные архитекторыnepster писал(а):Sam Dark писал(а):zf3 проще, ага Самопис — не вариант, если вы не готовы потратить на него пару-тройку лет чтобы получить нормальный набор фич и соответствующее популярным фреймворкам качество.
Александ, зря вы наверно так считаете. Сейчас все решается черзе независимые библиотеки, тоесть фактически берем MVC или что-то подобное, реализовываем поддержку composer и используем сторонние библиотеки без лишних 25 слоев абстракции.
Тот же RBAC, oauth2, mailer, спокойненько подключаем и работает практически на пряму.
Тут наверное главно не лажануть с архитектурой и писать как-то аккуратненько, по SOLID как полагается.
Что касается ZF, я его в глаза не видел, но судя по описанию и рекламы 3 версии, там как раз таки решили как говорят пришли к простате и независимым компонентам.
Yii2, конечно вещь кутая, но содержит много чего лишнего и ну прямо очень сильно подталкивает делать плохие вещи.
Re: Разделение приложения на слои
Здесь в треде затык конкретно в этой грани: что делать, если в любимом фреймворке «многие» вещи сделаны грамотно, а остальная половина «немногих» вещей - не так грамотно, как хотелось бы. Вот здесь и приходится либо терпеть, либо костылить на том, что есть, либо половину фреймворка под себя переписывать, либо другой фреймворк выбирать, либо свой фреймворк собирать из компонентов.mistbow писал(а):Проще фреймворком воспользоваться, где многие вещи уже сделаны грамотно и многими людьми выверены.))
Re: Разделение приложения на слои
Что-то я тут к языку Go присмотрелся) понравился он мне. Вот еще бы Yii2 на нем бы тоже хотелось бы иметь)
Re: Разделение приложения на слои
qiang как раз на нем щас пилит)mistbow писал(а):Что-то я тут к языку Go присмотрелся) понравился он мне. Вот еще бы Yii2 на нем бы тоже хотелось бы иметь)
Re: Разделение приложения на слои
А что пилит, если не секрет?zelenin писал(а):qiang как раз на нем щас пилит)mistbow писал(а):Что-то я тут к языку Go присмотрелся) понравился он мне. Вот еще бы Yii2 на нем бы тоже хотелось бы иметь)
Re: Разделение приложения на слои
Фреймворкmistbow писал(а):А что пилит, если не секрет?zelenin писал(а):qiang как раз на нем щас пилит)mistbow писал(а):Что-то я тут к языку Go присмотрелся) понравился он мне. Вот еще бы Yii2 на нем бы тоже хотелось бы иметь)
Re: Разделение приложения на слои
А где можно про эту работу почитать?
Re: Разделение приложения на слои
https://github.com/go-ozzomistbow писал(а):А где можно про эту работу почитать?
- samdark
- Администратор
- Сообщения: 9489
- Зарегистрирован: 2009.04.02, 13:46
- Откуда: Воронеж
- Контактная информация:
Re: Разделение приложения на слои
Набор либ по работе пилит...
Нравится Yii? Давайте сделаем его лучше!.
Re: Разделение приложения на слои
https://github.com/norzechowicz/mydrinks
отличное демо-приложение, реализующее принципы ddd и частично cqrs. На данный момент это лучшее и самое реальное, что есть на гитхабе.
отличное демо-приложение, реализующее принципы ddd и частично cqrs. На данный момент это лучшее и самое реальное, что есть на гитхабе.
- samdark
- Администратор
- Сообщения: 9489
- Зарегистрирован: 2009.04.02, 13:46
- Откуда: Воронеж
- Контактная информация:
Re: Разделение приложения на слои
И всё-равно оно не показывает, зачем... 90% кода там сложно реализует очень простые задачи.
Нравится Yii? Давайте сделаем его лучше!.
Re: Разделение приложения на слои
все верно. Приложение показывает КАК, а не ЗАЧЕМ. Показывать значение применения энтерпрайз-паттернов трудно на простом приложении, т.к. плюсы становятся видны только на крупном приложении. Поэтому пример полезен тем, кто понимает ЗАЧЕМ, но сомневается КАК.Sam Dark писал(а):И всё-равно оно не показывает, зачем... 90% кода там сложно реализует очень простые задачи.
-
- Сообщения: 3
- Зарегистрирован: 2016.04.08, 15:35
Re: Разделение приложения на слои
Отличная тема, прочел все 10 страниц, а так же ссылки которые были приведены в статье.
Решил, что для закрепления материала и для того что бы разобраться самому имеет смысл пересмотреть в свежих проектах свой код, нашел такой rest action
Попробовал его порефакторить в соотвествии с прочитанным, а так же не сильно уходить от Yii стиля - если это можно так назвать.
Так же после прочтения многих статей на тему репозитория, есть чувство, что использовать этот паттерн в Yii просто нет необходимости, частично AR, Query и Model реализуют эту задачу, насколько правильно ? Вопрос второй, но если есть желание или необходимость использовать именно этот паттерн, то наверное имеет смысл выбрать Laravel или SF2 где этот подход является более приемлемым. Поправьте пожалуйста если это не так.
Поэтому я решил, что наверное существующую логику action'а имеет смысл выделить просто в сервис после этого action стал намного меньше
а вся логика выборки переместилась в сервис
Вся логика инкапсулирована в одном месте, структура понятна, но ощущение монолитности кода не покидает и есть сомнения в плане тестирования такого кода.
У меня есть желание вынести find'ы в другой класс, какой это может быть класс ? Репозиторий ? Но если следовать этому паттерну, то в первую очередь репозиторий - это интерфейс, а не класс в названии которого есть слово "Repository", что я имею ввиду
Подобный класс мало что имеет общего с репозиторием, это просто хелпер который имеет статический метод.
Но тогда вопрос, куда выделить все find'ы ? Или использовать их на прямую в сервисах или выделять в какой-то отдельный класс как пример выше ?
Просто если реализовать для этой цели репозиторий, он возможно будет избыточным и тогда не совсем ясна роль AR и Query... получиться что-то типа
а в коде сервиса призойдут такие изменения
1. Избавляемся от методов, которые реализуют find'ы, а именно: getPromotedVehicles() и getRandomPromotedVehicles()
2. Метод getVehicles() приобретает след. вид
Конечно, вид этого метода может быть лучше, рассматривайте это как пример...
Получаются какие-то репозитории в репозиториях, но с другой стороны мы разделили ответственность и такой код будет проще тестировать.
p.s. немного запутался, а мысли смешаны, help!
Решил, что для закрепления материала и для того что бы разобраться самому имеет смысл пересмотреть в свежих проектах свой код, нашел такой rest action
Код: Выделить всё
namespace frontend\modules\v1\actions\vehicle;
use frontend\modules\v1\actions\BaseAction;
use frontend\modules\v1\models\PromotedVehicle;
use frontend\modules\v1\models\Vehicle;
use frontend\modules\v1\models\vehicle\PopularCar;
use frontend\modules\v1\models\vehicle\VehicleQuery;
use frontend\modules\v1\Module;
use yii\data\ArrayDataProvider;
class PopularAction extends BaseAction
{
/** @var array */
private $result = [];
public function run($limit = 5)
{
/** @var Vehicle $vehicle */
$vehicle = new $this->modelClass([
'scenario' => $this->scenario,
]);
/** @var PromotedVehicle|array $promotedVehicles */
$promotedVehicles = PromotedVehicle::find()
->with([
'car' => function (VehicleQuery $query) use ($vehicle) {
return $query->active()->inUse();
},
'car.carModel',
'car.carBrand',
])
->recently()
->last24Hours()
->limit($limit)
->all();
if ($promotedVehicles) {
foreach ($promotedVehicles as $promotedVehicle) {
$this->result[] = $promotedVehicle;
}
}
if (($cnt = count($promotedVehicles)) < $limit) {
/** @var PopularCar|array $populars */
$populars = $vehicle->find()
->with(['user', 'carModel', 'carBrand', 'commentCounter'])
->popular($vehicle->vehicle_type)
->language(Module::$langId)
->orderBy('rand()')
->limit((int)$limit - $cnt)
->all();
if ($populars) {
foreach ($populars as $popular) {
$this->result[] = $popular;
}
}
}
return new ArrayDataProvider([
'allModels' => $this->result,
'pagination' => false
]);
}
}
Так же после прочтения многих статей на тему репозитория, есть чувство, что использовать этот паттерн в Yii просто нет необходимости, частично AR, Query и Model реализуют эту задачу, насколько правильно ? Вопрос второй, но если есть желание или необходимость использовать именно этот паттерн, то наверное имеет смысл выбрать Laravel или SF2 где этот подход является более приемлемым. Поправьте пожалуйста если это не так.
Поэтому я решил, что наверное существующую логику action'а имеет смысл выделить просто в сервис после этого action стал намного меньше
Код: Выделить всё
namespace frontend\modules\v1\actions\vehicle;
use frontend\modules\v1\actions\BaseAction;
use frontend\modules\v1\models\Vehicle;
use frontend\modules\v1\services\vehicle\popular\PopularVehicleService;
use yii\data\ArrayDataProvider;
/**
* Class PopularAction
* @package frontend\modules\v1\actions\vehicle
*/
class PopularAction extends BaseAction
{
/** @var PopularVehicleService */
private $popularVehicleService;
public function run($limit = 5)
{
/** @var Vehicle $vehicle */
$vehicle = new $this->modelClass([
'scenario' => $this->scenario,
]);
$this->popularVehicleService = new PopularVehicleService($vehicle, ['limit' => $limit]);
return new ArrayDataProvider([
'allModels' => $this->popularVehicleService->getVehicles(),
'pagination' => false
]);
}
}
Код: Выделить всё
namespace frontend\modules\v1\services\vehicle\popular;
use frontend\modules\v1\models\PromotedVehicle;
use frontend\modules\v1\models\Vehicle;
use frontend\modules\v1\models\vehicle\VehicleQuery;
use frontend\modules\v1\Module;
use yii\base\Object;
use yii\helpers\ArrayHelper;
/**
* Class PopularVehicleService
* @package frontend\modules\v1\services\vehicle\popular
*/
class PopularVehicleService extends Object
{
/** @var int */
public $defaultLimit = 5;
/** @var int */
public $limit = 0;
/** @var Vehicle */
private $vehicle;
/** @var array */
private $vehicles = [];
public function __construct(Vehicle $vehicle, $config = [])
{
$this->vehicle = $vehicle;
parent::__construct($config);
}
public function getVehicles()
{
$this->fillVehicles($this->getPromotedVehicles());
if ($this->notFull()) { // fill the array up to the limit by random vehicles
$this->fillVehicles($this->getRandomPromotedVehicles());
}
return $this->vehicles;
}
/**
* @return array|\yii\db\ActiveRecord[]
*/
private function getPromotedVehicles()
{
$promoted = PromotedVehicle::find()
->joinWith([
'car' => function (VehicleQuery $query) {
return $query->select(['id'])->active()->inUse();
}
])
->recently()
->last24Hours()
->limit($this->getLimit())
->all();
$promotedIds = ArrayHelper::getColumn($promoted, function ($element) {
return $element['car_id'];
});
return $this->vehicle
->find()
->with(['user', 'carBrand', 'carModel', 'commentCounter', 'channel'])
->andWhere(['id' => $promotedIds])
->all();
}
/**
* @return array|\yii\db\ActiveRecord[]
*/
private function getRandomPromotedVehicles()
{
return $this->vehicle
->find()
->with(['user', 'carModel', 'carBrand', 'commentCounter', 'channel'])
->popular($this->vehicle->vehicle_type)
->language(Module::$langId)
->orderBy('rand()')
->limit($this->getRandLimit())
->all();
}
/**
* @return int
*/
private function getLimit()
{
return $this->limit ? $this->limit : $this->defaultLimit;
}
/**
* @return int
*/
private function getRandLimit()
{
return (int)($this->getLimit() - $this->countVehicles());
}
/**
* @param $data
*/
private function fillVehicles($data)
{
foreach ($data as $item) {
$this->addVehicle($item);
}
}
/**
* @param $item
*/
private function addVehicle($item)
{
$this->vehicles[] = $item;
}
/**
* @return int
*/
private function countVehicles()
{
return (int)count($this->vehicles);
}
/**
* @return bool
*/
private function notFull()
{
return $this->countVehicles() < $this->getLimit();
}
}
У меня есть желание вынести find'ы в другой класс, какой это может быть класс ? Репозиторий ? Но если следовать этому паттерну, то в первую очередь репозиторий - это интерфейс, а не класс в названии которого есть слово "Repository", что я имею ввиду
Код: Выделить всё
class SomeRepository {
public static function getById($id){
return Model::findOne($id);
}
}
Но тогда вопрос, куда выделить все find'ы ? Или использовать их на прямую в сервисах или выделять в какой-то отдельный класс как пример выше ?
Просто если реализовать для этой цели репозиторий, он возможно будет избыточным и тогда не совсем ясна роль AR и Query... получиться что-то типа
Код: Выделить всё
interface PromotedVehicleRepositoryInterface
{
public function getPromotedVehiclesIds($limit);
}
class PromotedVehicleRepository implements PromotedVehicleRepositoryInterface
{
public function getPromotedVehiclesIds($limit)
{
$promoted = PromotedVehicle::find()
->joinWith([
'car' => function (VehicleQuery $query) {
return $query->select(['id'])->active()->inUse();
}
])
->recently()
->last24Hours()
->limit($limit)
->all();
return $promotedIds = ArrayHelper::getColumn($promoted, function ($element) {
return $element['car_id'];
});
}
}
Код: Выделить всё
interface VehicleRepositoryInterface
{
public function getPromotedVehiclesByIds(array $ids, $limit);
public function getPromotedRandomVehicles($limit);
}
class VehicleRepository implements VehicleRepositoryInterface
{
public function getPromotedVehiclesByIds(array $ids, $limit)
{
return Vehicle::find()
->with(['user', 'carBrand', 'carModel', 'commentCounter', 'channel'])
->andWhere(['id' => $ids])// IN() condition
->all();
}
public function getPromotedRandomVehicles($limit)
{
return Vehicle::find()
->with(['user', 'carModel', 'carBrand', 'commentCounter', 'channel'])
->popular(Vehicle::TYPE_CAR)
->language(Module::$langId)
->orderBy('rand()')
->limit($limit)
->all();
}
}
1. Избавляемся от методов, которые реализуют find'ы, а именно: getPromotedVehicles() и getRandomPromotedVehicles()
2. Метод getVehicles() приобретает след. вид
Код: Выделить всё
public function getVehicles()
{
$vehicleRepo = new VehicleRepository();
$promotedRepo = new PromotedVehicleRepository();
$this->fillVehicles(
$vehicleRepo->getPromotedVehiclesByIds(
$promotedRepo->getPromotedVehiclesIds($this->getLimit()),
$this->getLimit()
)
);
if ($this->notFull()) { // fill the array up to the limit by random vehicles
$this->fillVehicles($vehicleRepo->getPromotedRandomVehicles($this->getRandLimit()));
}
return $this->vehicles;
}
Получаются какие-то репозитории в репозиториях, но с другой стороны мы разделили ответственность и такой код будет проще тестировать.
p.s. немного запутался, а мысли смешаны, help!
Re: Разделение приложения на слои
абсолютно неправильно. репозиторий - абстракция над хранилищем. AR - абстракции не дает.m.khartanovych писал(а):Так же после прочтения многих статей на тему репозитория, есть чувство, что использовать этот паттерн в Yii просто нет необходимости, частично AR, Query и Model реализуют эту задачу, насколько правильно ?
этот подход там применяется, посколько в их сообществах заботятся об архитектуре. Какой-то специальной заточки под репозитории нигде нету и не должно быть. Соответственно использовать можно где угодно.m.khartanovych писал(а):Вопрос второй, но если есть желание или необходимость использовать именно этот паттерн, то наверное имеет смысл выбрать Laravel или SF2 где этот подход является более приемлемым. Поправьте пожалуйста если это не так.
Код: Выделить всё
class PopularVehicleService extends Object
вообще браво. отличную аналитику провели.Вся логика инкапсулирована в одном месте, структура понятна, но ощущение монолитности кода не покидает и есть сомнения в плане тестирования такого кода.
У меня есть желание вынести find'ы в другой класс, какой это может быть класс ? Репозиторий ? Но если следовать этому паттерну, то в первую очередь репозиторий - это интерфейс, а не класс в названии которого есть слово "Repository", что я имею ввиду
Подобный класс мало что имеет общего с репозиторием, это просто хелпер который имеет статический метод.Код: Выделить всё
class SomeRepository { public static function getById($id){ return Model::findOne($id); } }
Но тогда вопрос, куда выделить все find'ы ? Или использовать их на прямую в сервисах или выделять в какой-то отдельный класс как пример выше ?
Просто если реализовать для этой цели репозиторий, он возможно будет избыточным и тогда не совсем ясна роль AR и Query... получиться что-то типа
Код: Выделить всё
interface PromotedVehicleRepositoryInterface { public function getPromotedVehiclesIds($limit); } class PromotedVehicleRepository implements PromotedVehicleRepositoryInterface { public function getPromotedVehiclesIds($limit) { $promoted = PromotedVehicle::find() ->joinWith([ 'car' => function (VehicleQuery $query) { return $query->select(['id'])->active()->inUse(); } ]) ->recently() ->last24Hours() ->limit($limit) ->all(); return $promotedIds = ArrayHelper::getColumn($promoted, function ($element) { return $element['car_id']; }); } }
а в коде сервиса призойдут такие измененияКод: Выделить всё
interface VehicleRepositoryInterface { public function getPromotedVehiclesByIds(array $ids, $limit); public function getPromotedRandomVehicles($limit); } class VehicleRepository implements VehicleRepositoryInterface { public function getPromotedVehiclesByIds(array $ids, $limit) { return Vehicle::find() ->with(['user', 'carBrand', 'carModel', 'commentCounter', 'channel']) ->andWhere(['id' => $ids])// IN() condition ->all(); } public function getPromotedRandomVehicles($limit) { return Vehicle::find() ->with(['user', 'carModel', 'carBrand', 'commentCounter', 'channel']) ->popular(Vehicle::TYPE_CAR) ->language(Module::$langId) ->orderBy('rand()') ->limit($limit) ->all(); } }
1. Избавляемся от методов, которые реализуют find'ы, а именно: getPromotedVehicles() и getRandomPromotedVehicles()
2. Метод getVehicles() приобретает след. вид
Конечно, вид этого метода может быть лучше, рассматривайте это как пример...Код: Выделить всё
public function getVehicles() { $vehicleRepo = new VehicleRepository(); $promotedRepo = new PromotedVehicleRepository(); $this->fillVehicles( $vehicleRepo->getPromotedVehiclesByIds( $promotedRepo->getPromotedVehiclesIds($this->getLimit()), $this->getLimit() ) ); if ($this->notFull()) { // fill the array up to the limit by random vehicles $this->fillVehicles($vehicleRepo->getPromotedRandomVehicles($this->getRandLimit())); } return $this->vehicles; }
Получаются какие-то репозитории в репозиториях, но с другой стороны мы разделили ответственность и такой код будет проще тестировать.
p.s. немного запутался, а мысли смешаны, help!
Репозиторий - абстракция над хранилищем. Соответственно мы везде закладываемся на интерфейс, и НЕ создаем экземпляр репозитория напрямую, иначе мы не сможем быстро подменить один репозиторий на другой.
Сервис+репозиторий - да, в вашем случае это похоже на репозиторий в репозиторие, но надо понимать что делает сервис и что репозитория. Репозиторий работает с хранилищем. Сервис осуществляет бизнес логику. В сервисе вы можете кроме сохранения через репозиторий сущности сохранить связанные сущности, отправить почту по факту сохранения, пересчитать какую-нибудь ставку, связанную с сохранением сущности (с помощью внедренного другого сервиса). В общем задача сервиса намного обширна. Если у вас пока нет таких бизнес требований - ничего.
И важный момент: используйте SOLID. Без этого у вас будет имитация архитектуры, которая не даст никаких плюсов, задумываемых ею. https://ru.wikipedia.org/wiki/%D0%9A%D0 ... 0.B8.D1.8F
То есть важно не просто репозиторий и сервис сделать, а понять зачем вы его сделали и чем он облегчит вам жизнь, кроме инкапсуляции какой-то логики.
-
- Сообщения: 3
- Зарегистрирован: 2016.04.08, 15:35
Re: Разделение приложения на слои
Спасибо за Ваш ответ.
Да, вы правы что AR не дает абстракции над хранилищем, но в Yii разве есть другой инструмент ?
Так уж получилось, что проект написан с использованием Yii фреймворка, мне не хочется внедрять что-то инородное, тратить на это время и деньги заказчика, но в то же время мне хочется поддерживать код в чистоте и порядке, но без фанатизма, так как ничего идеального не бывает.
Для меня, в контексте Yii, репозиторий нужен для того, что бы выделить туда find, save, update, возможно не правильно это называть репозиторием, можно дать другое название классу, но мне важно, что бы find, save, update были отделены от модели или query.
Сервис - тут все проще, обычно сервис затачивается под определенную задачу, в моем примере я создаю instance репозиторию внутри сервиса, но лучше было бы это инжектить в сервис и указывать не конкретный класс, а интерфейс
Вместо
я бы заменил на
В таком случае, мы отталкиваемся от интерфейса, а не от реализации, реализация сокрыта в конкретном классе и сервису все равно какой это класс, главное что у нас есть контроль типа, своего рода контракт.
Сервису важно что бы были соблюдены контракты - интерфейсы и его задача выполнить логику: заполнить массив, сделать проверку, что-то посчитать, отдать результат.
Так же у меня нет задачи разбивать на мелкие модули ради мелких модулей. Я это стараюсь делать для того, что бы:
1. классы имели свою ответственность
2. что бы не приходилось по всем контроллерам менять например query цепочку, а сделать это в одном месте
3. тестирование
4. рефакторинг
5. поддержка
По 4-му и 5-му пункту - благо в Yii есть встроенный инструмент для генерации документации классов, мне кажется это полезным.
Да, вы правы что AR не дает абстракции над хранилищем, но в Yii разве есть другой инструмент ?
Так уж получилось, что проект написан с использованием Yii фреймворка, мне не хочется внедрять что-то инородное, тратить на это время и деньги заказчика, но в то же время мне хочется поддерживать код в чистоте и порядке, но без фанатизма, так как ничего идеального не бывает.
Для меня, в контексте Yii, репозиторий нужен для того, что бы выделить туда find, save, update, возможно не правильно это называть репозиторием, можно дать другое название классу, но мне важно, что бы find, save, update были отделены от модели или query.
Сервис - тут все проще, обычно сервис затачивается под определенную задачу, в моем примере я создаю instance репозиторию внутри сервиса, но лучше было бы это инжектить в сервис и указывать не конкретный класс, а интерфейс
Вместо
Код: Выделить всё
// Метод сервиса
public function getVehicles()
{
$vehicleRepo = new VehicleRepository();
$promotedRepo = new PromotedVehicleRepository();
...
}
Код: Выделить всё
class PopularVehicleService extends Object
{
/** @var int */
public $defaultLimit = 5;
/** @var int */
public $limit = 0;
/** @var VehicleRepository */
private $vehicleRepo;
/** @var PromotedVehicleRepository */
private $promotedRepo
/** @var array */
private $vehicles = [];
public function __construct(PromotedVehicleRepositoryInterface $promotedRepo, VehicleRepositoryInterface $vehicleRepo, $config = [])
{
$this->promotedRepo = $promotedRepo;
$this->vehicleRepo = $vehicleRepo;
parent::__construct($config);
}
Сервису важно что бы были соблюдены контракты - интерфейсы и его задача выполнить логику: заполнить массив, сделать проверку, что-то посчитать, отдать результат.
Так же у меня нет задачи разбивать на мелкие модули ради мелких модулей. Я это стараюсь делать для того, что бы:
1. классы имели свою ответственность
2. что бы не приходилось по всем контроллерам менять например query цепочку, а сделать это в одном месте
3. тестирование
4. рефакторинг
5. поддержка
По 4-му и 5-му пункту - благо в Yii есть встроенный инструмент для генерации документации классов, мне кажется это полезным.
Re: Разделение приложения на слои
есть кверибилдер, есть прямые запросы в БД. В Симфони например вообще нет БД-слоя.m.khartanovych писал(а):Да, вы правы что AR не дает абстракции над хранилищем, но в Yii разве есть другой инструмент ?
но не в этом суть. Смысл репозитория в том, что вы через единый интерфейс можете общаться с хранилищем, зная что оттуда получите. А что там внутри (AR, PDO, http-клиент, file_get_contents) вы знать не должны. Таким образом вы можете иметь админку с репозиторием на AR, а фронт с репозиторием на голых запросах.
простой сервисный слой - уже неплохо.m.khartanovych писал(а):Так уж получилось, что проект написан с использованием Yii фреймворка, мне не хочется внедрять что-то инородное, тратить на это время и деньги заказчика, но в то же время мне хочется поддерживать код в чистоте и порядке, но без фанатизма, так как ничего идеального не бывает.
думаю, это банально должен быть PostService с методами find, save, update.m.khartanovych писал(а):Для меня, в контексте Yii, репозиторий нужен для того, что бы выделить туда find, save, update, возможно не правильно это называть репозиторием, можно дать другое название классу, но мне важно, что бы find, save, update были отделены от модели или query.
все верно. контракт это и называется.m.khartanovych писал(а):Сервис - тут все проще, обычно сервис затачивается под определенную задачу, в моем примере я создаю instance репозиторию внутри сервиса, но лучше было бы это инжектить в сервис и указывать не конкретный класс, а интерфейс
Вместоя бы заменил наКод: Выделить всё
// Метод сервиса public function getVehicles() { $vehicleRepo = new VehicleRepository(); $promotedRepo = new PromotedVehicleRepository(); ... }
В таком случае, мы отталкиваемся от интерфейса, а не от реализации, реализация сокрыта в конкретном классе и сервису все равно какой это класс, главное что у нас есть контроль типа, своего рода контракт.Код: Выделить всё
class PopularVehicleService extends Object { /** @var int */ public $defaultLimit = 5; /** @var int */ public $limit = 0; /** @var VehicleRepository */ private $vehicleRepo; /** @var PromotedVehicleRepository */ private $promotedRepo /** @var array */ private $vehicles = []; public function __construct(PromotedVehicleRepositoryInterface $promotedRepo, VehicleRepositoryInterface $vehicleRepo, $config = []) { $this->promotedRepo = $promotedRepo; $this->vehicleRepo = $vehicleRepo; parent::__construct($config); }