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

Вурдалак

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

От сюда и проистекает то, что в большинстве случает в Doctrine большинство entity - anemic entity.
http://thinkbeforecoding.com/post/2009/03/04/How-not-to-inject-services-in-entities
 

Вурдалак

Продвинутый новичок
Кстати, там чувак советует double dispatch, но уже относительно недавно появилось хипстерское (в плане, пока реально не видел, чтобы использовали) направление CQRS + Event Sourcing. Там вообще 1 модель чисто на запись, которая генерирует события, на которое можно подписаться и делать что угодно + N моделей для чтения (ViewModel). В теории интересно: https://speakerdeck.com/mathiasverraes/practical-event-sourcing Но на практике хз.
 
Последнее редактирование:

stalxed

Новичок
Вурдалак, это в теории.
На практике нужны сервисы, чтобы какое-то более менее вменяемое поведение сделать.
Блин, да просто тупо значение с конфига взять, которое влияет на вычисление.

Поэтому я обычно использую anemic entity + сервисы domain уровня.
Не получается у меня использовать rich entity :(
 

Вурдалак

Продвинутый новичок
stalxed, я тоже над этим неоднократно задумывался, мне кажется, что вполне приемлемо сделать domain service, если у тебя так есть некие зависимости. Но если ты внимательно почитаешь текст по ссылке, то ты можешь сделать это через double dispatch: ты передаешь сам сервис со зависимостями в метод модели, а она уже его использует.
 

fixxxer

К.О.
Партнер клуба
М, Double Dispatch я на практике и делаю, но мне это никогда не нравилось почему-то, как-то грязновато выглядит чисто интуитивно.
 

stalxed

Новичок
Вурдалак, за double dispatch спасибо, буду иметь в виду, может представится случай использовать.

Ещё недавний случай против rich entity.
Необходимо было сделать простую операцию - посчитать количество элементов в связанной коллекции(one-to-many), с определенным статусом.
Просто и легко?! Да! Самое оно для применения rich entity. НО! Нужно было это сделать для 10-100 записей, у каждой их которых может быть до 1000 дочерних.
Т.е. в память будет загружено до 100000 объектов, если обращаться к ним по ссылком, и выполнять подсчёты в родительской rich entity.

Т.е.против rich entity так же может выступать фактор оптимизации использования ресурсов.
 

Absinthe

жожо
Ещё раз привожу ссылку: 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 не пользуюсь, мне кажется ты лучше знать должен. Ж)
У меня DTO обескровлены. По руководству.

Просто если не Php Doctrine и если не Active Record, то что тогда?
Доктрина - дерьмо, но аналогов нет. Мышки кололись, плакали, но продолжали жрать кактусы.

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

grigori

( ͡° ͜ʖ ͡°)
Команда форума
чтобы Row Data Gateway остался исключительно на инфраструктурном уровне нужен DataMapper.
Т.е. у вас будет Order в domain layer, а в infrastructure layer будет OrderRDG и DataMapper выполняющий трансформации между Order и OrderRDG.
да, именно так и сделано. OrderMapper может быть реализован неявно, но он всегда существует. RDG не является сущностью бизнес-логики. Логика прописывается уровнем выше - DM или AR. Разница только в способе связи - через наследование или через соглашения.

Doctrine не требует сеттеров и геттеров, она пользуется рефлексией.
мне одному кажется, что выбор инструмента конкретной реализации не имеет значения?
 
Последнее редактирование:

Вурдалак

Продвинутый новичок
У меня DTO обескровлены. По руководству.
А я думал у тебя своя голова на плечах. Например, вот мнение самого автора Doctrine (2012): http://www.whitewashing.de/2012/08/22/building_an_object_model__no_setters_allowed.html

Всегда считал такое использование неканоническим и неправильным.
То есть тебе нравится anemic? В радикальном случае — просто набор пабликов, да? Нормик?
 

Absinthe

жожо
А я думал у тебя своя голова на плечах. Например, вот мнение самого автора Doctrine (2012): http://www.whitewashing.de/2012/08/22/building_an_object_model__no_setters_allowed.html
А что будет с течением времени, когда появятся сотни новых применений этой DTO? Если она будет не обескровленной, то через несколько человеколет ее размер должен сильно вырасти.
Когда у DTO появляется связь с десятком других DTO за человекогод (или месяца два реального времени), единственным способом не превращать ее в GodObject - это не давать ей логику вовнутрь, а использовать сервисы.
 

Вурдалак

Продвинутый новичок
Absinthe, ты прибегаешь к софистике, ты утверждаешь следующее:
У меня DTO обескровлены. По руководству.
Я не могу оспорить это утверждение, кроме как поправить, что не «по руководству», а «по определению». Речь не о том что такое DTO, это знают дети в детском саду, а каким должна быть модель-сущность: DTO (=== anemic) или аналогом того, что выше с Route.

А что будет с течением времени, когда появятся сотни новых применений этой DTO? Если она будет не обескровленной, то через несколько человеколет ее размер должен сильно вырасти.
Когда у DTO появляется связь с десятком других DTO за человекогод (или месяца два реального времени), единственным способом не превращать ее в GodObject - это не давать ей логику вовнутрь, а использовать сервисы.
Декомпозиция (выделение новых сущностей, value object'ов; вынесение каких-то вещей в свои спецификации, если требуется), рефакторинг модели в соответствии с изменениями в бизнес-логики/требованиях.

Послушай, я не могу тебе всё обосновать, потому что в реальном DDD-проекте я сам никогда не работал, я просто вижу реальные проблемы в таких тупых идиотских подходах типа полностью пустых моделей без логики, без поддержки своих собственных инвариантов (одна из самых главных для меня проблем). Генерируем сеттеры, геттеры, ололо, вперед, сервисы. Да говно на большом проекте получается, вот и всё, труднее разбираться, проще ошибиться. Я видел много кейсов, когда rich model и always valid models приносили бы реальный профит в моих конкретных задачах, поэтому я всячески стараюсь двигаться в этом направлении.
 

Absinthe

жожо
я просто вижу реальные проблемы в таких тупых идиотских подходах типа полностью пустых моделей без логики
С того, что я такую проблему обозначил, мы тему и начали.
Поэтому я предпочитаю логику в модели иметь ;)
Но не всегда решать мне.

А уже сохраняется ли она сама (AR), или это делаете третья сторона (меппер) - другой вопрос.
 

Вурдалак

Продвинутый новичок
А что будет с течением времени, когда появятся сотни новых применений этой DTO?
Есть ещё такая штука, как http://martinfowler.com/bliki/BoundedContext.html Если у тебя в рамках приложения с одной сущностью есть миллион применений, то такая модель в любом случае перегружена. Поэтому крупный сайт почти всегда можно поделить на кучу более мелких domain'ов: биллинг, игры, чат, etc. Та же самая декомпозиция.
 

Absinthe

жожо
Есть ещё такая штука, как http://martinfowler.com/bliki/BoundedContext.html Если у тебя в рамках приложения с одной сущностью есть миллион применений, то такая модель в любом случае перегружена. Поэтому крупный сайт почти всегда можно поделить на кучу более мелких domain'ов: биллинг, игры, чат, etc. Та же самая декомпозиция.
Но есть же какая-нибудь центральная сущность (или несколько), вокруг которых существуют все остальные. Например, пользователь, у которого есть куча всего, и постоянно появляется что-то новое, что к нему относится.
Про паттерн почитаю, спасибо.
 

Вурдалак

Продвинутый новичок
Но есть же какая-нибудь центральная сущность (или несколько), вокруг которых существуют все остальные. Например, пользователь, у которого есть куча всего, и постоянно появляется что-то новое, что к нему относится.
Про паттерн почитаю, спасибо.
Ну так я понимаю у тебя будет две сущности User, которые могут отличаться. Например, в контексте биллинга мне не будет интересна (наверное) раса пользователя. Если только это не WhitePowerBillingDomain.
 

Absinthe

жожо
Ну так я понимаю у тебя будет две сущности User, которые могут отличаться. Например, в контексте биллинга мне не будет интересна (наверное) раса пользователя. Если только это не WhitePowerBillingDomain.
А если всегда нужны несколько (минимум 5) основных полей?
Я в итоге даже на доктрине это все наследовал, чтобы в наследниках поля добавлять :D

Но фактически эти наследники были связаны с исходной таблицей (да, я знаю про три типа наследования в SQL).
 
Последнее редактирование:

Вурдалак

Продвинутый новичок
А если всегда нужны несколько (минимум 5) основных полей?
Я не знаю, it depends. Если эти понятия можно объединить в одно (например, UserCredentials, UserProfile, etc.), то можно вынести в отдельный value object. Наследование — это обычно всегда крайний вариант :)

Я тут погуглил: http://culttt.com/2014/11/26/strategies-integrating-bounded-contexts/ — там вообще своя атмосфера, вечерком на phpclub'е такое не переваришь.
 

Absinthe

жожо
Я не знаю, it depends. Если эти понятия можно объединить в одно (например, UserCredentials, UserProfile, etc.), то можно вынести в отдельный value object. Наследование — это обычно всегда крайний вариант :)
В итоге нашел довольно удобный способ для доктрины:
1. Добавляется новая сущность, которыя хранит элементы, нужные только для модуля профиля: Profile\UserProfile.
2. Базовая сущность Base\User наследуется языковым наследованием в Profile\User исключительно для добавления связи с Profile\UserProfile. Ну и логики, если она не обескровлена. При этом новая сущность связана с прежним источником данных.
3. Вся работа в модуле профиля осуществляется с использованием класса Profile\User.

В итоге класс Base\User наконец-то закрыт.

P.S. Кстати, по DDD, архитектуре есть чо?
Я видел только Patterns of Enterprise Application Architecture
 
Последнее редактирование:
Сверху