2amigos/yii2-usuario

Обсуждаем, как правильно строить приложения
anton_z
Сообщения: 483
Зарегистрирован: 2017.01.15, 15:01

Re: 2amigos/yii2-usuario

Сообщение anton_z »

Мой ответ в основном не про эти модули, а про осторожность, которую стоит проявлять, когда начинаешь заниматься "архитектурой"

Да, в usuario с интерфейсами и паттернами явный перебор. Архитектура для архитектуры, а не для решения задачи.
Зачем выделены мегаобщие интерфейсы типа StrategyInterface? Заменить любую стратегию на любую другую? Декорировать все и всем? Абсурд.
Пытаются создать возможность заменить что угодно на что угодно, создать абсолютную гибкость. Это ошибка. Нельзя предусмотреть все и вся наперед. Проблема известна как premature generalization. По мне нужно при проектировании определить что конкретно может измениться в будущем и оставить швы под это. Или добавлять швы, выделяя интерфейсы по мере появления необходимости в других реализациях/декорировании. Изолироваться от всего и вся создавая интерфейсы сразу и для всего - игрушки.

Согласен с BrusSENS, нужны в общем-то библиотеки, а не модули. Архитектурные решения должны приниматься исходя из бизнес-задач. Универсальные модули - миф. Разобьются о первое кастомное бизнес-требование. От них больше вреда, чем пользы в нетиповом проекте. Для типовых сайтов, да могут подойти. Но там разговоры про архитектуру не имеют смысла. На CMS стандартные магазины хорошо делаются.

О "stateless" сервисах и DiC
---------------------------------------

Будьте осторожны! В последнее время пошла мода как можно больше классов делать т.н. "stateless" и пихать все в контейнеры. Мода пришла из Java в Symfony, сейчас разносится по многим фреймворкам, в т.ч. yii. Это порочная практика. Stateless объектов не должно существовать. Иначе это не объекты, а функции. На самом деле в контейнер можно и нужно засовывать не так много. Например репозитории - они инкапсулируют соединение с БД и название таблицы/таблиц. Это их состояние. Они не stateless. Такие классы разработаны правильно и их можно пихать к контейнер. Моделируют шлюз к ханилищу. Что дают - шов для замены слоя работы с бд.
Пример-антагонист: PasswordChangerService. Инкапсулирует только UserRepository. У него есть метод execute который вытаскивает юзера и меняет ему пароль. Такие "stateless" сервисы возвращают нас к процедурному программированию. Полученный сервис это процедура, а сущность юзера это данные. Это не объекты, а функции и структуры данных, написанные в виде классов. ООП должно моделировать объекты реального мира. PasswordChangerService ничего не моделирует. Это алгоритм, а не объект.

Больно видедь конфигурацию контейнеров с сотнями инжекций, из которых реально понадобятся 2-3. а времени на разбор такого кода уйдет в 2-3 раза больше.

Про DDD и повальную моду на него
--------------------------------------------------

Люди кидаются в DDD без изучения ООП и воспринимают его с тактической стороны. Типа надо создавать сущности, сервисы, шины, события и тогда у нас будет DDD и это хорошо. Не будет. DDD это в основном про аналитическое проектирование, как разговаривать с менеджментом, как писать юзкейсы (на бумаге) а не "как назвать классы". Начинать лучше с книг по объектно-ориентированному анализу и проектированию. Вернон после этого будет читаться совсем по-другому. Хороший объектно-ориентированный код можно получить и вовсе без DDD, DiC и шин (польза шин вообще сомнительна).

Про шины
--------------

Реальных юзкейсов для шин вообще не очень много. А их пихают сейчас в обычные сайты. Это вообще уже смешно. Жаль только, что с этим потом приходится кому-то разбираться.
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: 2amigos/yii2-usuario

Сообщение ElisDN »

anton_z писал(а): 2017.09.13, 06:26 Да, в usuario с интерфейсами и паттернами явный перебор. Зачем выделены мегаобщие интерфейсы типа StrategyInterface? Заменить любую стратегию на любую другую? Декорировать все и всем? Абсурд. Пытаются создать возможность заменить что угодно на что угодно, создать абсолютную гибкость. Это ошибка.
Да, перебор. Более того, многие зачем-то пытаются делать интерфейсы даже для PasswordChangerService помимо наследование для ничем не связанных типов. Это не меньший абсурд.
anton_z писал(а): 2017.09.13, 06:26 Будьте осторожны! В последнее время пошла мода как можно больше классов делать т.н. "stateless" и пихать все в контейнеры... Stateless объектов не должно существовать. Иначе это не объекты, а функции.
Да, это процедуры и функции с настройками, сделанные для выполнения каких-либо операций. Без изменения своего внутреннего состояния. Как Вы ниже сказали, оформленные в виде классов для поддержки автозагрузки, типизации, и иньекций их друг в друга. Функции с настройками можно заменить замыканиями, но их неудобно будет доставать и тестировать.
anton_z писал(а): 2017.09.13, 06:26 Например репозитории - они инкапсулируют соединение с БД и название таблицы/таблиц. Это их состояние. Они не stateless. Такие классы разработаны правильно и их можно пихать к контейнер. Моделируют шлюз к ханилищу. Что дают - шов для замены слоя работы с бд.
У репозитория нет модификаторов, изменяющих его внутреннее состояние. И нет привычного нам динамического состояния. Они, так сказать, dynamic stateless.
anton_z писал(а): 2017.09.13, 06:26 Пример-антагонист: PasswordChangerService. Инкапсулирует только UserRepository. У него есть метод execute который вытаскивает юзера и меняет ему пароль. Такие "stateless" сервисы возвращают нас к процедурному программированию. Полученный сервис это процедура, а сущность юзера это данные. Это не объекты, а функции и структуры данных, написанные в виде классов. ООП должно моделировать объекты реального мира. PasswordChangerService ничего не моделирует.
Да, это процедура, реализующая и инкапсулирующая конкретный use case:

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

(new PasswordChangerService($repository, $mailer, $timeout, $email))->run()
легко приводимая к виду:

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

changePassword($repository, $mailer, $timeout, $email)
или к аналогичному статическому "методу":

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

PasswordChanger::change($repository, $mailer, $timeout, $email)
Но если не смешивать статические зависимости и динамические данные, то можно этот объект сделать без динамического (изменяемого) состояния:

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

(new PasswordChangerService($repository, $mailer, $timeout))->change($email)
или привести к более логичному замыканию:

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

$changePassword = passwordChangerFactory($repository, $mailer, $timeout);
...
$changePassword($email);
Отделение настроек от данных позволит создать этот элемент один раз и ипользовать с одними настройками многократно для разных данных:

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

$this->passwordChanger->change($email)
вынеся весь процесс его конструирования в контейнер, чтобы не тянуть до него $timeout из конфигов по всему коду.

А так в любом приложении есть данные, модели объектов реального мира и процессы по манипулированию этими данными и объектами. Глобально это и приводит к логическому разделению на структуры, объекты и процедуры. А то, что для всего этого многим привычнее использовать классы - это уже технические издержки. Главное самому понимать, где у нас DTO, где объект и где процедура.
anton_z писал(а): 2017.09.13, 06:26 Больно видедь конфигурацию контейнеров с сотнями инжекций, из которых реально понадобятся 2-3. а времени на разбор такого кода уйдет в 2-3 раза больше.
Эти времена прошли. Autowiring позволяет сократить конфигурацию до описания десятка правил только для нестандартных случаев. Остальное подхватывается на лету.
anton_z писал(а): 2017.09.13, 06:26 Люди кидаются в DDD без изучения ООП и воспринимают его с тактической стороны. Типа надо создавать сущности, сервисы, шины, события и тогда у нас будет DDD и это хорошо. Не будет.
Да, любая бездумная деятельность приводит к недоразумениям:

Изображение
anton_z
Сообщения: 483
Зарегистрирован: 2017.01.15, 15:01

Re: 2amigos/yii2-usuario

Сообщение anton_z »

ElisDN писал(а): 2017.09.13, 09:34
У репозитория нет модификаторов, изменяющих его внутреннее состояние. И нет привычного нам динамического состояния. Они, так сказать, dynamic stateless.
Я бы назвал его неизменяемым объектом (immutable object).
ElisDN писал(а): 2017.09.13, 09:34 А так в любом приложении есть данные, модели объектов реального мира и процессы по манипулированию этими данными и объектами. Глобально это и приводит к логическому разделению на структуры, объекты и процедуры. А то, что для всего этого многим привычнее использовать классы - это уже технические издержки. Главное самому понимать, где у нас DTO, где объект и где процедура.
В этом нет ничего плохого, и это работает, но это не ООП. Как только речь заходит о разделении на объекты с данными и объекты-сервисы/процедуры/процессы по манипулированию (как угодно), это уже не ООП. Нельзя олицетворять объекты с данными. Они больше про поведение. Объектами нельзя манипулировать как данными. Мы должны вызывать их поведение. А не так - достал объект из БД, изменил его поля, положил в БД. Это не ООП, а процедурный подход с классами. В ООП вся соль в том как распределить поведение между объектами.

P.S. Я сам так думал - есть stateless и statefull объекты. Потом пришел к выводу, что это наследие процедурного подхода.
Последний раз редактировалось anton_z 2017.09.13, 11:16, всего редактировалось 6 раз.
anton_z
Сообщения: 483
Зарегистрирован: 2017.01.15, 15:01

Re: 2amigos/yii2-usuario

Сообщение anton_z »

ElisDN писал(а): 2017.09.13, 09:34
Эти времена прошли. Autowiring позволяет сократить конфигурацию до описания десятка правил только для нестандартных случаев. Остальное подхватывается на лету.
Awtowiring не панацея. Рефлексия дурно пахнет. Не подходит для интерфейсов. Добавляет неявность. Не использую. В основном по последней причине. По мне так если получается много инжекций - получился OOP bloated код, слишком гибкий. Для фреймворков такое может и полезно (Я не пишу фреймворки.). Для приложений - вредно.
anton_z
Сообщения: 483
Зарегистрирован: 2017.01.15, 15:01

Re: 2amigos/yii2-usuario

Сообщение anton_z »

ElisDN писал(а): 2017.09.13, 09:34 Да, любая бездумная деятельность приводит к недоразумениям:
Ну почему бездумная. Люди ищут, что кто-нибудь им расскажет, как писать хороший код. Есть мнения, что DDD панацея. Раскручивается эти мнения авторами книг по DDD.
Аватара пользователя
BrusSENS
Сообщения: 565
Зарегистрирован: 2012.07.26, 06:51
Откуда: Новороссийск
Контактная информация:

Re: 2amigos/yii2-usuario

Сообщение BrusSENS »

anton_z писал(а): 2017.09.13, 06:26 Люди кидаются в DDD без изучения ООП
Без знания и практики ООП - даже в Yii лезть провальная идея сама по себе.
anton_z писал(а): 2017.09.13, 06:26 Хороший объектно-ориентированный код можно получить и вовсе без шин (польза шин вообще сомнительна).

Реальных юзкейсов для шин вообще не очень много. А их пихают сейчас в обычные сайты. Это вообще уже смешно. Жаль только, что с этим потом приходится кому-то разбираться.
Как по мне - командная шина удобная штука, но вот шины ещё и на выборку данных, на валидацию - пока так и не понял в чём профит. Сделать более DDD? Проще заинжектить репозиторий и выбрать всё что нужно.
anton_z писал(а): 2017.09.13, 11:53 Ну почему бездумная. Люди ищут, что кто-нибудь им расскажет, как писать хороший код. Есть мнения, что DDD панацея. Раскручивается эти мнения авторами книг по DDD.
DDD - это вообще, как по мне, парадигма нужная в особо редких случаях. Репозитории, командная шина, сервисы - это удобно без какого-либо DDD, паттерны никто не отменял и DDD'шными они не стали. Хотя опять же, злоупотреблять ими не стоит. Зачем плодить сервисы для выборки данных? Не пойму, хотя видел подобное на форуме. Но опять же, SOLID это не плохо. Можно писать SOLID'ный код и не приходя к DDD. Просто с умом и в меру.
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
Аватара пользователя
Faenir
Сообщения: 292
Зарегистрирован: 2010.01.06, 01:46
Откуда: Симферополь

Re: 2amigos/yii2-usuario

Сообщение Faenir »

anton_z, спасибо, очень полезно!
Больно видедь конфигурацию контейнеров с сотнями инжекций, из которых реально понадобятся 2-3. а времени на разбор такого кода уйдет в 2-3 раза больше.
Я так понимаю, вы про Bootstrap с кучей "$di->set(...)"? Я вот тоже не понял, для чего регистрировать вообще все эти классы в бутстрапе ($defaults, $routes, $di->set). Это для гибкости что ли? Чтобы была возможность подменить абсолютно любой класс модуля?
anton_z
Сообщения: 483
Зарегистрирован: 2017.01.15, 15:01

Re: 2amigos/yii2-usuario

Сообщение anton_z »

Faenir писал(а): 2017.09.13, 13:49 Я так понимаю, вы про Bootstrap с кучей "$di->set(...)"? Я вот тоже не понял, для чего регистрировать вообще все эти классы в бутстрапе ($defaults, $routes, $di->set). Это для гибкости что ли? Чтобы была возможность подменить абсолютно любой класс модуля?
Да. Про него и аналоги из других контейнеров.
anton_z
Сообщения: 483
Зарегистрирован: 2017.01.15, 15:01

Re: 2amigos/yii2-usuario

Сообщение anton_z »

BrusSENS писал(а): 2017.09.13, 13:40
Без знания и практики ООП - даже в Yii лезть провальная идея сама по себе.
Про yii несогласен. Фреймворк как раз и предлагает быстрый старт для начинающих разработчиков. Можно разобраться с синтаксисом классов и вперед. Быстрый результат не отпугнет и придаст уверенности. Для входа фреймворк действительно неплох. ООП начнет приходить если читать книги и развиваться дальше, не останавливаясь на достигнутом.

Материалы про DDD надо читать между строк не воспринимая буквально примеры кода.

Оговорюсь, что не претендую на истину в последней истанции, так как никто не может дать точного определения ООП и где начинается "я знаю ООП".
Аватара пользователя
BrusSENS
Сообщения: 565
Зарегистрирован: 2012.07.26, 06:51
Откуда: Новороссийск
Контактная информация:

Re: 2amigos/yii2-usuario

Сообщение BrusSENS »

anton_z писал(а): 2017.09.13, 14:31 Про yii несогласен. Фреймворк как раз и предлагает быстрый старт для начинающих разработчиков. Можно разобраться с синтаксисом классов и вперед. Быстрый результат не отпугнет и придаст уверенности. Для входа фреймворк действительно неплох. ООП начнет приходить если читать книги и развиваться дальше, не останавливаясь на достигнутом.

Материалы про DDD надо читать между строк не воспринимая буквально примеры кода.
С одной стороны да, но с другой стороны начинающие не имея совершенно понятия, например, о построении структуры БД для проекта, или имея, но довольно скудное представление об этом, начинают юзать AR и сталкиваются с кучей проблем.
Из этого вываливаются вопросы и том, чем отличаются with() и joinWIth() и другие подобные непонятки. Отсутствие понимания проектирования на самом простом уровне - это нежелание учить матчасть, просто урывая непонятные куски кода, в итоге - нежелание читать официальные доки.
Потом начитавшись про DDD, как это круто, начинают городить различные репозитории с AR, не понимая, что это совершенно противоположные паттерны. После чего переходят на другие инструменты, разочаровываясь в Yii, а порой и в php в целом. А порой и начинаются вопросы о том, в какую папку положить тот или иной класс, не понимая, что файловая структура !== архитектура.
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
Аватара пользователя
maleks
Сообщения: 1985
Зарегистрирован: 2012.12.26, 12:56

Re: 2amigos/yii2-usuario

Сообщение maleks »

Faenir писал(а): 2017.09.13, 13:49 Я так понимаю, вы про Bootstrap с кучей "$di->set(...)"? Я вот тоже не понял, для чего регистрировать вообще все эти классы в бутстрапе ($defaults, $routes, $di->set). Это для гибкости что ли? Чтобы была возможность подменить абсолютно любой класс модуля?
Если смотреть по логике работы DIContainer::get то вот эти "пустые" определения - $di->set(Event\FormEvent::class); - вроде и не нужны совсем, и без них оно так же работает.
Может автор держит это просто как напоминание какие классы можно через DI настроить или даже подменить на потомки (т.к. он не использует интерфейсы).

Смотрю люди так делают, а насколько вы считаете правильным через DI подменять неабстрактный класс на его потомок?
Как то по мне выглядит не наглядно. В Yii::createObject точно указываем какой класс хотим, а появится вместо него другой, хоть и совместимый.
Yii2 universal module sceleton - for basic and advanced templates
anton_z
Сообщения: 483
Зарегистрирован: 2017.01.15, 15:01

Re: 2amigos/yii2-usuario

Сообщение anton_z »

Интерфейсы конечно надо, ну или абстрактные классы хотя бы. По мне так вообще от конкретных классов лучше не наследоваться. Либо abstract, либо final. Так надежнее всего и легче разбираться. С методами также. Но это если сознательно делать шов для гибкости а не просто "гибкость это хорошо, делаем везде"
Аватара пользователя
BrusSENS
Сообщения: 565
Зарегистрирован: 2012.07.26, 06:51
Откуда: Новороссийск
Контактная информация:

Re: 2amigos/yii2-usuario

Сообщение BrusSENS »

anton_z писал(а): 2017.09.16, 11:57 Интерфейсы конечно надо, ну или абстрактные классы хотя бы. По мне так вообще от конкретных классов лучше не наследоваться. Либо abstract, либо final. Так надежнее всего и легче разбираться. С методами также. Но это если сознательно делать шов для гибкости а не просто "гибкость это хорошо, делаем везде"
Вот рассуждал я как-то на тему абстрактных классов и интерфейсов. Собственно в чём разница, помимо технической части, на мой взгляд:
Абстрактный класс - прежде всего чётко определяет тип объекта. Например: abstract class Tree, мы уже точно знаем, что объект, унаследованный от него будет именно деревом, например березка: class Birch extends Tree. Т.е. мы имеем абстрактные методы, присущие для данного типа объекта, вроде Tree::grow().
Интерфейсы - наоборот не навязывают строгую типизацию объекта, посему они более глобальны. Например: имеем interface Substance, который всего лишь говорит о том, что объект является субстанцией, а какой именно - это абсолютно не важно. Нет чёткого определения принадлежности объекта.

Надеюсь понятно изъяснился.

P.S.: Всё вышеизложенное - ИМХО.
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: 2amigos/yii2-usuario

Сообщение ElisDN »

BrusSENS писал(а): 2017.09.16, 13:39 Надеюсь понятно изъяснился.
Хорошие интерфейсы несут только чистый смысл, описывая абстрактный тип данных. Вроде yii\rbac\AuthManagerInterface. При их создании надо думать. Думать до написания кода методов, а не после, чтобы по несколько раз их не переписывать. И TDD здесь весьма кстати для реализации подхода "семь раз отмерь, один отрежь".

А абстрактный класс в дополнение к типу на практике чаще оказывается не продуманной абстракцией, а кучей общего кода, который было лень декомпозировать в порыве адского быстрого кодинга и который судорожно бросили в базовый класс вроде yii\gii\Generator. А для особо ленивых как псевдоспособ избавления от копипасты придумали трейты. Это, как люблю говорить, уже "семь раз отрежь, один отмерь".
Nex-Otaku
Сообщения: 831
Зарегистрирован: 2016.07.09, 21:07

Re: 2amigos/yii2-usuario

Сообщение Nex-Otaku »

абстрактный класс в дополнение к типу на практике чаще оказывается не продуманной абстракцией, а кучей общего кода, который было лень декомпозировать в порыве адского быстрого кодинга и который судорожно бросили в базовый класс вроде yii\gii\Generator.
После прочтения этих строк до меня наконец дошло, что мне не нравилось в моём собственном абстрактном классе в одном из модулей )
Чувствовал, что с ним что-то не то, но не понимал, а теперь ясно стало )
Спасибо )
Аватара пользователя
BrusSENS
Сообщения: 565
Зарегистрирован: 2012.07.26, 06:51
Откуда: Новороссийск
Контактная информация:

Re: 2amigos/yii2-usuario

Сообщение BrusSENS »

ElisDN писал(а): 2017.09.16, 20:06 Хорошие интерфейсы несут только чистый смысл, описывая абстрактный тип данных. Вроде yii\rbac\AuthManagerInterface. При их создании надо думать. Думать до написания кода методов, а не после, чтобы по несколько раз их не переписывать. И TDD здесь весьма кстати для реализации подхода "семь раз отмерь, один отрежь".

А абстрактный класс в дополнение к типу на практике чаще оказывается не продуманной абстракцией, а кучей общего кода, который было лень декомпозировать в порыве адского быстрого кодинга и который судорожно бросили в базовый класс вроде yii\gii\Generator. А для особо ленивых как псевдоспособ избавления от копипасты придумали трейты. Это, как люблю говорить, уже "семь раз отрежь, один отмерь".
Примерно об этом я и сказал в посте выше) Посему абстрактные классы для одних целей, а интерфейс совершенно для других.
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
Ответить