Тонкий контроллер, всё в моделях

Вурдалак

Продвинутый новичок
А вытаскивать его в паблик - не, ну так вроде уже никто не делает, это ж в чистом виде олдскульная процедурщина со структурой и функциями получается.
Это вообще очень популярный подход - так как требует минимальной нагрузки на головной мозг.
Так он популярный или никто так не делает?

я бы не сказал, что геттерно-сеттерная проблема специфична именно для AR
Я и не утверждаю. Блин, опять AR, can you be just cool, OK?
 

fixxxer

К.О.
Партнер клуба
Да пофигу на AR, это частный случай.

Под "не делает" я имел ввиду вырожденный случай, который я часто видел почему-то в ActionScript, где в явном виде создается class FooValueObject (да-да, именно ValueObject-ами это называют), являющийся по сути структурой - состоящий только из публичных полей, - и везде таскается. Наверное, в каком-нибудь популярном туториале так написано было.

А под геттерно-сеттерным подходом я имею ввиду вещи ширше - все случаи, когда, условно говоря, в разных местах пишется $foo->bar (или $foo->getBar() / $foo->setBar()), когда следовало бы в одном месте написать $this->bar.

Скажем, в твоем примере с $user->isBanned() и $user->rename() исключение вполне может кидаться и в сеттерах, но проблема геттерно-сеттерной архитектуры никуда не денется (а сеттеры обрастут дерьмом, да).
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Вурдалак, предлагаю закрыть тему, я не хочу чтобы ты переходил на личности. Стоит писать более спокойно и терпеливо.

Для меня anemic model, value object, декларация структуры в доктрине, структура AR - это одно и то же. Создано разными способами, написано на разных языках, но цель, структура и использование одинаково.

Есть сущность, которая содержит структуру и данные, соответствующие структуре таблицы в базе. ORM-движок генерит объекты из этих классов: заполняет данными из базы или пишет из них в базу. Это может быть реализовано через наследование, декларацию, трейт, аннотации, структуру можно считывать из базы, как угодно.
Может быть, геттеры - зло, неважно. Структура, описанная на yml, так же точно может быть реализована трейтом или предком. В любом случае есть какой-то служебный value object, который обеспечивает маппинг. По архитектуре он может быть недоступен вне DM или работать по соглашениям при генерации запроса в движке ORM, но он все-равно есть, и это - анемичная модель.
Раз у нас доменная логика работает поверх анемичной модели - отличие от AR для меня несколько нивелируется.
Единственный вариант не создавать такой объект с данными - писать запросы руками без мапинга.
 
Последнее редактирование:

Absinthe

жожо
В том-то и дело, что людям нравится пихать логику в сервисы
А это плохо при использовании DM?

а сеттеры и геттеры они очень любят, я не буду показывать пальцем, но точно помню такие высказанные мысли у некоторых участников этого форума. Да и потом, все известные мне AR наружу всё равно выпячивают setters/getters, и по закону Мёрфи обязательно будет код, который юзает их напрямую, если только ты не работаешь один.
Что имеется ввиду под геттерами/сеттерами? Получение свойств объекта?
Можно ограничить класс, чтобы он не все свойства наружу отдавал.

Doctrine использует рефлексию, она не требует сеттеры, геттеры
Трубует :) Нельзя сделать свойства пабликами как в обычных DTO из-за lazy loading и проксирования связанных объектов.

DM (persistence ignorance, separation of concerns) vs AR (нарушение SRP, тесная связь с хранилищем, проникновение деталей реализации в domain layer -> нарушение инкапсуляции);
В AR тоже есть separation of concerns, в виде подключаемых блоков логики. Например, Loggable, Dropboxable и подобные примеси.

Вурдалак, предлагаю закрыть тему, я не хочу чтобы ты переходил на личности. Стоит писать более спокойно и терпеливо.
В чем проблема не переходить на личности?
 

stalxed

Новичок
Для меня anemic model, value object, декларация структуры в доктрине, структура AR - это одно и то же. Создано разными способами, написано на разных языках, но цель, структура и использование одинаково.
Ужас...

pattern Value Object - это нечто не имеющее идентификатор. Например Range, Money, Date, etc.
декларация структуры в доктрине - это Metadata Mapping, который используется Data Mapper. Data Mapper это подвид pattern Mapper. Суть его сделать 2 объекта независимыми друг от друга и от Mapper.
pattern Active Record = pattern Row Data Gateway + бизнес логика
pattern Row Data Gateway = смешивание двух слоёв воедино: Domain Layer и Infrastructure Layer. Что само по себе ужасно. Исключение: небольшие проекты.

Где взаимосвязь между этими patterns?!

А вот antipattern Anemic Domain Model весьма интересно!
Я себе его представляю так:
1) Есть Domain Layer
Например
PHP:
class DriverLicense
{
    private $price;
    public function __construct($price){$this->price = $price;}
    public function getPrice(return $this->price;}
}
2) Есть Application Layer
PHP:
class DriverLicinseService
{
    public function buy($honestPoliceman = false, $greedyPoliceman = true)
    {
         if ($honestPoliceman) {
             return new ImpossibleBuyException();
         }

         return new DriverLicense($greedyPoliceman ? 5000: 1000);
    }
Здесь Application Layer занимается бизнес логикой, так как сам высчитывает цену, тогда как это должен делать Domain Layer.
Но что если я сделаю сервис в слое Domain Layer, который будет вычислять цену?
Или блин, чтоб не было этого антипаттерна я должен закинуть домен логику именно в DriverLicense?
 

Vano

Новичок
Это что, у меня аура такая? в половине моих тем, после третьего сообщения люди начинают спорить)
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
>декларация структуры в доктрине - это Metadata Mapping, который используется Data Mapper. Data Mapper это подвид pattern Mapper. Суть его сделать 2 объекта независимыми друг от друга и от Mapper.
> Где взаимосвязь между этими patterns?!

Структура данных в ActiveRecord - это Metadata Mapping. Все точно так же.

pattern Active Record = pattern Row Data Gateway + бизнес логика
pattern Row Data Gateway = смешивание двух слоёв воедино: Domain Layer и Infrastructure Layer.
RDG не содержит Domain Layer.
если "Domain Layer" = "бизнес логика" , то RDG не может содержать в себе DL, иначе он превращается в AR
 
Последнее редактирование:

stalxed

Новичок
grigori, Active Record соединяет в себе общение с базой данных(Infrastructure Layer) и доменную модель(Domain Layer).
Если используйте Active Record, то автоматически лишаетесь чёткого слоя Domain Layer.
Лично я себе представляю так - применили Active Record(или Row Data Gateway) - теперь у вас не многослойное приложение.
В угоду скорости это не плохо, но нужно понимать эту кровавую цену.

< Структура данных в ActiveRecord - это Metadata Mapping. Все точно так же.
Нет, не точно также.
В случае Data Mapper метаданные использует DataMapper, от которого не зависит сущность(entity).
Т.е. Data Mapper = Infrastructure Layer он же считывает метаданные
Сущность остаётся независимой от Data Mapper, а следовательно от Infrastructure Layer, и остаётся в Domain Layer.

В случае c Active Record ваша сущность берёт на себя сама считывание метаданных, и соответственно, операции с БД, а значит одновременно является и Infrastructure Layer и Domain Layer.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
применили Active Record(но не Row Data Gateway) - теперь у вас не многослойное приложение.
с этим не спорю :)

Метаданные точно так же считывается в Infrastructure Layer и в отдельном объекте хранятся.
Разница в том, что в AR интерфес для использования в ORM реализован в предке, а не в соседнем кошерном объекте на соглашениях.
В реальной жизни сущностей, которые не зависят от структуры данных, не бывает.
Править структуру одновременно с логикой надо в любом случае.

Я не говорю, что разницы в реализации нет, но для меня она условно религиозная.

мне кажется, мы уже по кругу пошли
 
Последнее редактирование:

stalxed

Новичок
Может лучше вернёмся к антипаттерну Anemic Domain Model?
Допустим есть два варианта подсчета суммы:
Первый:
PHP:
Domain Layer:
class Order
{
    private $orderItems;
    public function getOrderItems(){return $this->orderItems;}
    public function getPrice()
    {
        // здесь мега умное вычисление конечной цены
    }
}
Application Layer:
class BillingService
{
    public function pay(Order $order)
    {
        $order->getClient()->subtractFromBalance($order->getPrice());
    }
}
Второй:
PHP:
Domain Layer:
class Order
{
    private $orderItems;
    public function getOrderItems(){return $this->orderItems;}
}
class PriceCalculator
{
    public function calculate(Order $order)
    {
         // здесь мега умное вычисление конечной цены
         return $price;
    }
}
Application Layer:
class BillingService
{
     private $priceCalculator;
    public function __consturct(PriceCalculator $c)
    {
        $this->priceCalculator = $c
    }
    public function pay(Order $order)
    {
        $price = $this->priceCalculator->calculate($order);
        $order->getClient()->subtractFromBalance($price);
    }
}
В обоих случаях подсчет суммы идёт в домене. Но во втором случае подсчет суммы идёт в сервисе Domain Layerа, а в первом в самой сущности(entity).

Получается ли второй пример примером антипаттерна Anemic Domain Model?
Вообще что такое Domain Model? Это сумма различных элементов Domain Layer(entity, value object, factory, сервисы уровня domain model, etc) или это именно entity?
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
class Order у тебя - анемичная модель, а в чем вопрос?
 

stalxed

Новичок
Вопрос в том что такое модель.

Эрик Эванс:
Модель - это дистиллированное знание. Модель представляет собой согласованный между разработчиками способ структуризации знаний из предметной области, а также выделения элементов представляющих наибольший интерес.
Т.е. Domain Model - это и сущность Order и сервис domain уровня PriceCalculator.
Они вместе составляют модель. Значит это модель не анемичная.

Или в паттерне подразумевается анемичная сущность(entity)?

Погуглил, цитата Фаулера:
What's worse, many people think that anemic objects are real objects, and thus completely miss the point of what object-oriented design is all about.
Анемичный объект - звучит лучше(опять название паттерну придумывали задницей - Anemic Domain Model, почему было не назвать Anemic Domain Object?),. Да Order во втором случае анемичный объект.

Но что плохого в анемичных объектах?
 
Последнее редактирование:

stalxed

Новичок
RDG не содержит Domain Layer.
Как раз сущности и связи между ними, отражающие предметную область, и есть часть Domain Layer.
Соответственно, чтобы Row Data Gateway остался исключительно на инфраструктурном уровне нужен DataMapper.
Т.е. у вас будет Order в domain layer, а в infrastructure layer будет OrderRDG и DataMapper выполняющий трансформации между Order и OrderRDG.
 

Вурдалак

Продвинутый новичок
Что имеется ввиду под геттерами/сеттерами? Получение свойств объекта?
Методы один-ко-одному со списком свойств класса: либо set$propertName и get$propertyName, либо __set/__get, не суть.

Трубует :) Нельзя сделать свойства пабликами как в обычных DTO из-за lazy loading и проксирования связанных объектов.
Ещё раз привожу ссылку: https://github.com/leopro/trip-planner/blob/master/src/Leopro/TripPlanner/Domain/Entity/Route.php — это anemic или rich? Если anemic, то почему ты так считаешь? Если rich, то почему ты говоришь, что Doctrine не позволяет делать rich? Если я захочу, я сделаю геттер. Не захочу — никто не заставляет, иначе говоря, от меня Doctrine не требует сеттеров и геттеров, она пользуется рефлексией. А что до lazy load, то, по-моему, Doctrine насрать на то сеттер там или что-то другое: просто любое обращение к public-методу будет требовать загрузки.

Кстати, я Doctrine не пользуюсь, мне кажется ты лучше знать должен. Ж)

В AR тоже есть separation of concerns, в виде подключаемых блоков логики. Например, Loggable, Dropboxable и подобные примеси.
Я рад за вас и AR, живите счастливо, но я в гости я к вам никогда не зайду. :) К anemic/rich это отношения по сути не имеет. Что до трейтов, они, говоря откровенно, сделаны именно для anemic: setter, getter + свойство. Да, да, ты их будешь переименовывать, менять область видимости, ога :), сэкономишь полминуты, а может даже наоборот, потеряешь. И ради чего, спрашивается? Читабельность хуже + искусственные ограничения (column length для 2-х таблиц разный не сделаешь, будешь думать над тем, «а стоит ли оно того трогать трейт, пусть лучше длина будет одинаковая, пофиг, да насрать вообще, не заметят»).
 

stalxed

Новичок
Если anemic, то почему ты так считаешь? Если rich, то почему ты говоришь, что Doctrine не позволяет делать rich?.
В Doctrine сложно инджектить в entity, и авторы не рекомендую это делать, если я правильно помню.
А без этого не значения с конфига не прочитать, да даже банальный translator не вызвать.
Без зависимостей далеко не уедешь, т.е. не сделать хорошую rich entity.

От сюда и проистекает то, что в большинстве случает в Doctrine большинство entity - anemic entity.
 

fixxxer

К.О.
Партнер клуба
О, вот это хороший вопрос. Почему-то все избегают инъекций в entities, какой фреймворк ни возьми - это и "не рекомендуется", и нормального штатного способа это сделать не предоставляется. Почему?
 
Сверху