Service Locator Repository injection with trait

WMix

герр M:)ller
Партнер клуба
300 c комментом ? и как ты уместишь туда и сущность и запросы?
простая связка типа заказ и позиции
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Эти рекомендации старше меня, наверное, они прописаны во всех хороших стандартах кодинга. Я их соблюдаю.
Классов должно быть много, и они должны быть маленькими. Если класс больше 200-300 строк, или если метод не помещается на экран целиком - пора рефакторить.
Комментарии не учитываются. Может быть, например, sql на пару экранов, или длинный список, но один. Остальной код должен уложиться в 200 строк. Большие массивы и конфиги нужно инклюдить.
Конечно, в класс помещается только CRUD или только одна логическая операция. Сущность - отдельно, коллекция - отдельно. Таких разных маленьких классов у меня бывало несколько сотен, и никакого ада.
 
Последнее редактирование:

Вурдалак

Продвинутый новичок
Про 200 строчек кода и большие массивы, которые нужно «инклюдить» — это тянет на ответ Junior'а, когда его спросили про критерии качества кода. Более опытный разработчик бы вспомнил про SOLID. Потому что просто иметь много классов по 200 строчек недостаточно. Нужно понимать причину боли, которая обычно находится в самом дизайне.

Реальность такова, что с таким «ручным» ActiveRecord, с магическими трейтами, глобалсами и т.д., любой [поддерживаемый] проект в долгосрочной перспективе будет загнивать. Если в качестве аргументации тебе ближе argumentum ad verecundiam, то послушай вон @fixxxer'а.
 
Последнее редактирование модератором:

fixxxer

К.О.
Партнер клуба
Но это же совершенно другая проблема, которая никак не связана с темой. Хитрость надо выносить. Если действие выходит из CRUD - нужно уходить наверх.
Вот в том и проблема. Бизнес-логика редко ложится на CRUD. Если бы ложилась, никаких бы проблем с банальным AR не возникало, можно было бы, как в упомянутом мной в соседней теме StrongLoop, писать все приложение декларативным json-конфигом. Но обычно все сложнее. А как только логику, затрагивающую одну сущность, размыли по куче сервисов - становится все сложнее поддерживать состояние сущности в консистентном состоянии, приходится помнить, что все эти сервисы делают, как и почему.

Лично для меня этаким моментом "вот оно!" в DDD было понимание, что такое aggregate root. Причем я ведь в свое время своим умом почти дошел до этого, одного маленького шага не хватило. А потом соблазнился кажущейся простотой рельсов. :)

Конечно, в класс помещается только CRUD или только одна логическая операция. Сущность - отдельно, коллекция - отдельно. Таких разных маленьких классов у меня бывало несколько сотен, и никакого ада.
Оу, я на этой идее даже целый фреймворк писал. Не, будет ад, будет, просто поверь мне, пока не поздно! =)
 
Последнее редактирование:

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Интересно.
Другой вопрос. Проекты ж разные бывают. Во всяких админках автосгенеренный ActiveRecord на декларациях работает нормально годами, и есть не просит.
Ты рассматриваешь разделение проекта на части, микросервисы? Размазывание логики можно ограничить.
 

fixxxer

К.О.
Партнер клуба
Так говно в рамках одного bounded context. Если прямое управление состоянием entities размыть по микросервисам - лучше точно не будет :)
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Состояние сущности или aggregate - это набор значений полей структуры.
Управление состоянием делается через API aggregate root. При изменении логики будет меняться внутренняя структура aggregate. Откуда же появится размытие?
 

fixxxer

К.О.
Партнер клуба
Ниоткуда не появится, только если этому правилу четко следовать, неизбежно придешь к DDD :)
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
а как это правило мешает объединять в классе с данными sql? :)
 
  • Like
Реакции: AmdY

grigori

( ͡° ͜ʖ ͡°)
Команда форума
о, другой разговор! попробую, вдруг получится удобно
 

fixxxer

К.О.
Партнер клуба
Сразу же первый вопрос - откуда в статических методах entities возьмется database connection.

Фаулер этот вопрос, кстати, в PoEAA (где у него классический AR с ручным SQL) просто игнорирует. Есть откуда-то и всё. ;)
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
вот я предлагаю магически - при первом обращении трейт присваивает его свойству объекта в соответствии с конфигом инъекций
 

fixxxer

К.О.
Партнер клуба
Я понимаю, что можно через lsb и скрытый SL, но это все too much magic, а если потом кэширование надо итд - разрастется это все до неприличия и будет запутанно как в yii с его созданиями инстансов самого себя:)

Ну и самая большая опасность - с таким подходом велик соблазн сделать query builder и геттеры-сеттеры торчащими наружу, и получится в итоге "где-то анемик, а где-то нет", что намного хуже, чем просто anemic
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
В чем разница с инъекциями снаружи?
Точно такой же magic - по конфигу DI выставляются поля объектов через сеттеры. Вызов сеттеров? Это деталь реализации, можно сделать вызов сеттеров, если они есть в классе.
 

Вурдалак

Продвинутый новичок
В чем разница с инъекциями снаружи?
Точно такой же magic - по конфигу выставляются поля через сеттеры. Вызов сеттеров? Это деталь реализации, можно сделать вызов сеттеров, если они есть в классе.
Это некорректный вопрос, так как мы не делаем инъекции в сущность с помощью DIC.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Кстати, а зачем тебе database connection именно в статических методах?
С трейтом все отлично сводится к созданию инстанса aggregate root и наполнению им самого себя.
Обычно именованные фабрики нужны для инициализации, а тут, выходит, не нужны. Я как-раз начал писать, и убрал все static function. Могу показать в коде.
 
Последнее редактирование:

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Это некорректный вопрос, так как мы не делаем инъекции в сущность с помощью DIC.
Конечно, инъекция делается специальным объектом по конфигу, и он берет сервисы из контейнера. Это не меняет суть вопроса - в чем разница, если инъекцию выполняет трейт?
 

Вурдалак

Продвинутый новичок
О какой инъекции идёт речь?

Вот пример сущности:
PHP:
final class User
{
    public static function register(int $userId, string $name) {
        return new self($userId, $name);
    }

    public function rename(string $newName) {
        // ...
    }
}
Тут инъекция нигде не нужна.
 

MiksIr

miksir@home:~$
Конечно, инъекция делается специальным объектом по конфигу, и он берет сервисы из контейнера. Это не меняет суть вопроса - в чем разница, если инъекцию выполняет трейт?
Разница в том, что ты возлагаешь на объект, пусть и посредством трейта для уменьшения копипасты, обязанность разрешения собственных зависимостей.
И в том, что объект получается мертво прибит по имени к этому трейту. Тогда как в случае контейнеров тебе ничего не мешает создать два разных контейнера с разными зависимостями и создать два объекта одного класса через два разных контейнера.
Как-то так мне видится.
 
Сверху