Как назвать метод?

Вурдалак

Продвинутый новичок
А профит в чем?
Redjik, это я к вот этому:
Угу, тот кто пользуется Doctrine об этом прекрасно знает. Слоёная архитектура это ад в случае багов
«Ты не прав, потому что Doctrine с багами».

В принципе, приведённый fixxxer'ом пример библиотеки почти то. А QB торчать не будет, потому что интерфейс такого не позволит (interface UserRepository).

Хотя это всё на любителя, я бы скорее заюзал Doctrine.
 

fixxxer

К.О.
Партнер клуба
Вообще, наверное, доктрину уже стоит еще раз попробовать. Я вот рискнул, пока что-то сложно говорить с уверенностью, только начал - но вроде и ничего, если внутрь не смотреть. В последний раз-то пробовал, когда sf2 еще в бете был, офигел с багодрома. Щас лучше.
 

AmdY

Пью пиво
Команда форума
Нормальная слоёная архитектура — это просто. Слои — это результат декомпозиции. Если у тебя ад, значит ты не умеешь её готовить.
Вот потому, что ты всегда переводишь стрелки на "у вас", я привёл конкретный пример с doctrine. Это багодромище даже на стабильной версии было и баги были очень заковырыстые, но всё равно ты перевёл стрелки на "у вас" и "Eloquent".
 

fixxxer

К.О.
Партнер клуба
интерфейс такого не позволит
php - не java, не позволит здравый смысл, IDE или хук с правой :) Понятно, что не проблема, но все равно не нравится, что торчит. Как-то неаккуратненько.
 

fixxxer

К.О.
Партнер клуба
К вопросу инъекций в Entities. Первый же пример в analogueorm/analogue:

PHP:
class User extends Entity {

    public function __construct($email, $password)
    {
        $this->email = $email;
        $this->password = \Hash::make($password);
    }

}
Не хочу фасад, хочу Illuminate\Contracts\Hashing\Hasher $hasher. Что делать? setPassword($plainPassword, Hasher $hasher)? Ну хз.
 
Последнее редактирование:

Вурдалак

Продвинутый новичок
php - не java, не позволит здравый смысл, IDE или хук с правой
Если цель что-то сломать, то и в Java можно через рефлексию. Вызов несуществующего у интерфейса метода это в любом случае серьёзный фейл. На такие вещи надо бы снифферы натравливать, чтобы pull request не прошёл.

Понятно, что не проблема, но все равно не нравится, что торчит. Как-то неаккуратненько.
Мне вот, если честно, пофиг на аккуратность в инфраструктуре. Это то место, где скрываются кривые и не очень внешние библиотеки. Там даже фасады норм. :)

Не хочу фасад, хочу Illuminate\Contracts\Hashing\Hasher $hasher. Что делать? setPassword($plainPassword, Hasher $hasher)? Ну хз.
$user->changePasswordHash($hash)? :)
 

Redjik

Джедай-мастер
2 вопроса:
1) Есть товар и категория
У товара должны быть методы attachCategory, detachCategory
У категории addProduct, removeProduct
И как их подружить, чтобы состояние изменялось в обеих моделях, когда меняют одну?

2) У Лео есть спорный момент с валидацией даты.
Как заоверрайдить эту валидацию на уровне инфраструктуры?
Ну предположим в категории 1ккк товаров. При добавлении, по логике Лео, мы будем форычем пробегаться по всему 1ккк товаров, чтобы не получить ProductIsAlreadyInCategoryException
 

stalxed

Новичок
Redjik, по первому.
В терминах доктрины существуют Owning Side и Inverse Side.
Т.е. мы должны выбрать главное направление ассоциации, и выполнять обновление через него.
Если Owning Side Product, то будет так:
PHP:
class Product
{
    private $categories;
    public function attachCategory(Category $category)
    {
        $this->categories[] = $category;
        $category->addProduct($this);
    }
}
class Category
{
     private $products;
    public function addProduct(Procuct $product)
    {
        $this->products[] = $product;
    }
}
Хотя если заморочиться, то наверное можно сделать с обоих сторон обновление, например так:
PHP:
class Product
{
    private $categories;
    public function attachCategory(Category $category)
    {
        if (! $this->categories->contains($category)) {  
            $this->categories[] = $category;
            $category->addProduct($this);
        }
    }
}
class Category
{
     private $products;
    public function addProduct(Procuct $product)
    {
        if (! $this->products->contains($product) {
            $this->products[] = $product;
            $product->attachCategory($this);
        }
    }
}
Но я бы не стал так делать...

А вообще, Эванс пишет:
Существует как минимум три способа сделать ассоциации более удобными и управляемыми.
  1. Свести отношения к однонаправленным, прослеживаемым в одном направлении.
  2. Добавить квалификаторы, тем самым снижая кратность.
  3. Устранить несущественные ассоциации.
Может быть хватит одного направления? Я как-то раньше лепил повсюду ассоциации только двухсторонние, сейчас понял, что зачастую вполне хватает и одной стороны...

По второму, что за лео?
 

Redjik

Джедай-мастер
ну тоже так делаю, но ощущение говененькое =)

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

в Доктрине там есть взаимные привязки, но это опять уже уровень инфраструктуры ... и чот приуныл =))
 

stalxed

Новичок
Вот потому, что ты всегда переводишь стрелки на "у вас", я привёл конкретный пример с doctrine. Это багодромище даже на стабильной версии было и баги были очень заковырыстые, но всё равно ты перевёл стрелки на "у вас" и "Eloquent".
Где баги?
 

stalxed

Новичок
Redjik, ну в случае many-to-many можно самому выбирать Owning Side.
Посмотреть какая сторона по бизнес правилам больше годится для Owning Side, например Product.
А дальше действительно эксепшены:

PHP:
class Product
{
    private $categories;
    public function attachCategory(Category $category)
    {
        $this->categories[] = $category;
        $category->addProduct($this);
    }
    public function containCategory(Category $category)
    {
        return $this->categories->contains($category);
    }
}
class Category
{
     private $products;
    public function addProduct(Product $product)
    {
        if (! $product->containCategory($this)) {
            throw new AssociationException('Add product on Inverse Side');
        }
        $this->products[] = $product;
    }
}
 

Вурдалак

Продвинутый новичок
Redjik, а категории обязательно иметь свойство products, если там могут быть тысячи товаров?
PHP:
final class AddProductToCategoryUseCase
{
    private $transactionManager;
    private $productRepository;
    private $categoryRepository;

    public function __construct(
        TransactionManager $transactionManager,
        ProductRepository $productRepository,
        CategoryRepository $categoryRepository
    )
    {
        // ...
    }

    public function handle(AddProductCommand $command)
    {
        $this->transactionManager->transaction(function () use ($command) {
            $product = $this->productRepository->find($command->productId);
            $category = $this->categoryRepository->find($command->categoryId);
            $product->addTo($category); // @throws ProductAlreadyInCategory

            $this->productRepository->save($product);
        });
    }
}
А если нужно найти товары по категории, то можно написать $productRepository->findByCategoryId($categoryId).
 

stalxed

Новичок
Вурдалак, а удобно писать кучу подобных use case?
Т.е. не легче сделать сервис ProductService в в нём уже метод addToCategory(Category $category)
 

Redjik

Джедай-мастер
ну да, ты же уровень инфраструткуры задействуешь, и мне тоже это кажется логичным, но получается Anemic Domain Model
давай не мой возьмем сфеерически конский пример, а тот, что у Лео, вот ссылку выше кидал.

С одной стороны это поведение и должно быть на уровне домена, но если ног (Leg) уже овер дофига, то проверка немного жестковата...

UPD. блин чото я затупил тут =))) это ты на первый вопрос отвечал оказывается, а я начал про второй говорить....

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

stalxed

Новичок
Да, переход от взаимодействия по связям к репозиториям приводит к Anemic Domain Model.
И таки ради производительности это...
Меня это бесит, когда нужно посчитать суммы по какому-то полю, или количество по параметру. Вот и думаешь - грузить ради этого элементы по связи, или запрос к репозиторию.
Присматриваюсь к этому решению. Всё хочу поиграться.
Судя по всему они написали к доктрине виртуальные поля, которые собирают агрегационные значения.
 

Вурдалак

Продвинутый новичок
Вурдалак, а удобно писать кучу подобных use case?
Т.е. не легче сделать сервис ProductService в в нём уже метод addToCategory(Category $category)
На каждый use case ты будешь добавлять новый метод, код будет разрастаться вертикально, в конструкторе будет много зависимостей, которые для конкретного use case не нужны. Это нарушение SRP.

Мне кажется, лучше добавить в PhpStorm шаблон для class UseCase :)

ну да, ты же уровень инфраструткуры задействуешь
Транзакции — это не совсем инфраструктура. Мы должны на уровне приложения (application layer) понимать должны ли изменения быть целостными или необязательно. Причем это необязательно MySQL-транзакции, может быть у нас такая NoSQL-база, которая их умеет.

давай не мой возьмем сфеерически конский пример, а тот, что у Лео, вот ссылку выше кидал.
У leo решение для конкретной бизнес-проблемы. Может ли такое быть, что у маршрута в реальной жизни будут сотни пересадок? Мне кажется, что это сомнительно. В любом случае, можно явно договориться с бизнесом: разрабатывая модель будет нормально работать с такими-то ограничениями и прямо там поставить условие if (count($routeLegs) > 1000). Модель не обязана быть универсальной, в реальной жизни приходится, конечно, идти на компромиссы. Если менеджер хочет, например, что-то слишком жирного, то ты же сначала обсудишь с ним какие на это потребуется ресурсы, какие риски будут и т.д., вполне вероятно ты его отговоришь :)
 

Redjik

Джедай-мастер
Если менеджер хочет, например, что-то слишком жирного, то ты же сначала обсудишь с ним какие на это потребуется ресурсы, какие риски будут и т.д., вполне вероятно ты его отговоришь
Ахаха было такое, менеджеры хотели фичу... причем не сказали нам бизнес задачу, а тупо сказали, нам нужна вот такая фича - типа пишите код так то.
Ну мы с ребятами почесали репу и выставили время 4 человекомесяца.
После этого нам сразу рассказали бизнес задачу, на ее реализацию ушло намного меньше времени :D

У leo решение для конкретной бизнес-проблемы. Может ли такое быть, что у маршрута в реальной жизни будут сотни пересадок? Мне кажется, что это сомнительно. В любом случае, можно явно договориться с бизнесом: разрабатывая модель будет нормально работать с такими-то ограничениями и прямо там поставить условие if (count($routeLegs) > 1000). Модель не обязана быть универсальной, в реальной жизни приходится, конечно, идти на компромиссы. Если менеджер хочет, например, что-то слишком жирного, то ты же сначала обсудишь с ним какие на это потребуется ресурсы, какие риски будут и т.д., вполне вероятно ты его отговоришь :)
да это понятно, но как вообще с валидацией на уникальность поступать ... отдавать на откуп инфраструтуре и вообще не заморачиваться в домене?
 
Сверху