Бизнес логика и данные (активная запись, ORM и т.д.)

Бизнес логика и данные (активная запись, ORM и т.д.)

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

Во всех своих проектах я использую гибрид паттерна активная запись и ORM. У меня существует класс, реализующий маппинг для единичной таблицы (без внешних связей) с CRUD методами, конфигурирующийся при помощи xml схемы. Данный класс наследуется классами домена и в большинстве случаев этого достаточно.

Однако, если необходимо расширить функциональность CRUD методов, либо добавить дополнительные методы, работающие с БД, приходится реализовывать их в конечном классе домена, что не есть очень хорошо. Напрашивается решение - вынести работу с БД в отдельный слой и пользоваться методами классов этого слоя в классах домена. Т.е. в конечном классе CRUD методы будут делегировать методам прослойки и если понадобится реализовать дополнительную функциональности, изменениям подвергнется только класс прослойки.

Кто что может сказать по этому вопросу? Насколько оправданно создание дополнительного слоя? Может есть какие-то стандартные паттерны для решения подобных задач? Обратился к Фаулеру, вроде бы описываемое мной решение называется Table Gateway, но почему-то его рекомендуется использовать с сценарием транзакции, а не с моделью предметной области.

-~{}~ 17.05.05 21:14:

И в продолжение темы. Небольшой пример (стандартный): музыкальный архив. Для простоты две сущности: композиция и альбом, данные хранятся в разных таблицах (album, song). Два варианта реализации (пишу на перле, думаю всем понятно будет) добавления композиции.

Вариант первый
Код:
my $album = Album->new($request->getParam("artist_id"));
if (!$album) {
   $self->error("Invalid album");
   return 0;
}

my $song = Song->new();
$song->setAlbum($album->getId());
$song->setTitle($request->getParam("song_title"));
$song->setLength(...);
$song->save();
Вариант второй
Код:
my $album = Album->new($request->getParam("artist_id"));
...
$song->setTitle($request->getParam("song_title"));
$song->setLength(...);

$album->addSong($song);
И первый и второй варианты используют два паттерна: сценарий транзакций и активная запись. Первый вариант мне кажется более гибким, но менее правильным с точки зрения ООП. Какие мысли по этому вопросу и кто чем пользуется?
 

Gas

может по одной?
Напрашивается решение - вынести работу с БД в отдельный слой и пользоваться методами классов этого слоя в классах домена. Т.е. в конечном классе CRUD методы будут делегировать методам прослойки и если понадобится реализовать дополнительную функциональности, изменениям подвергнется только класс прослойки.
Имхо.
Если имеются несколько логически связанных классов, которым нужны данные не только из своей таблицы, то создавать прослойку которая реализует взаимодействие между ними очень даже нормально. Для одного же класса создавать прослойку как-то смысла не вижу, лучше переопределить CRUD методы или добавить новые и всё.
 

Макс

Старожил PHPClub
ИМХО в связанных объектах важно не столько добавление, сколько организация выборки связанных объектов.

Для добавления, первый вариант мне кажется вполне нормальным и простым в реализации.
 
Для выборки я использую активные шаблоны, Helper объекты и вот этот вот паттерн:
http://2netfly.com/cmf/dp.gif. Очень удобно и решает многие проблемы. Более подробно в соседней теме =)

-~{}~ 17.05.05 23:07:

Только хелпер у меня манипулирует не доменными объектами, а либо строит сам запросы, либо обращается за помощью к DAO.
 

pachanga

Новичок
Re: Бизнес логика и данные (активная запись, ORM и т.д.)

Автор оригинала: 2NetFly
И первый и второй варианты используют два паттерна: сценарий транзакций и активная запись. Первый вариант мне кажется более гибким, но менее правильным с точки зрения ООП. Какие мысли по этому вопросу и кто чем пользуется?
Здесь нет "серебряной пули" или лучшего решения, на самом деле оба паттерна ActiveRecord и DataMapper могут успешно применяться, здесь все зависит только от сложности реализуемой бизнес модели.

В простом случае - ActiveRecord, как только появляются сложные зависимости, связи и правила в бизнес модели, то без DataMapper просто никак.

Кстати, Фаулер не считает зазорным использование в проекте одновременно обоих паттернов.

После долгого использования некоторого суррогата ActiveRecord в своих проектах мы все же сделали выбор в пользу DataMapper. Причем вся прослойка от UnitOfWork до набора базовых DataMappers нами была написана самими, и все еще находится в активной разработке. Мы стараемся сделать максимально простую реализацию ORM реализацию, без OQL и прочих тяжко реализуемых в PHP(в плане скорости) "красивостей". Посмотрим, что из этого выйдет.
 
А что касательно второй части вопроса? В описанном мной примере я склоняюсь ко второму варианту т.е. позволить объектам взаимодействовать между собой т.к. они являются частью одной системы и по одиночке использоваться не могут. Но как быть, например, c классами конечной CMS, которые отвечают за комментарии и документы (банальный пример). Стоит ли их связывать друг с другом? Ведь при добавлении новой сущности, которую можно комментировать, придется вносить изменения в ее реализацию и наоборот, при добавлении новой функциональности (например, рейтинга) придется вносить изменения во все уже существующие классы.

-~{}~ 18.05.05 20:06:

Опять же в продолжение темы, куда помещать методы, например, переноса композиции из одного альбома в другой, проверки существования композиции с таким же именем и т.д. Интересны любые мысли по данному вопросу.
 

pachanga

Новичок
Автор оригинала: 2NetFly
А что касательно второй части вопроса? В описанном мной примере я склоняюсь ко второму варианту т.е. позволить объектам взаимодействовать между собой т.к. они являются частью одной системы и по одиночке использоваться не могут. Но как быть, например, c классами конечной CMS, которые отвечают за комментарии и документы (банальный пример). Стоит ли их связывать друг с другом? Ведь при добавлении новой сущности, которую можно комментировать, придется вносить изменения в ее реализацию и наоборот, при добавлении новой функциональности (например, рейтинга) придется вносить изменения во все уже существующие классы.
В случае CMS у нас получилось так, что 99% доменных объектов являются контейнерами для данных, т.к у них нет ярко выраженного поведения. И я в этом ничего не вижу плохого, тем более что для написания мапперов для таких объектов требуется несколько строчек кода(просто сделать наследника от некоторого базового маппера).

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

В этом плане очень рекомендую почитать Фаулера, Patterns of Enterprise Application Architecture
 

lovchy

nacido para cifrar
2NetFly
Вот проверку на существование композиции надо в маппер выносить. Как вариант -- проверка на существование отловом исключения при попытке создания резолвом, но это небольшой оверхед + неудобство в расширении. А перемещение я бы сделал методом композиции. Почему-то мне кажется более логичным двигать элемент списка от лица элемента, а не от сущности списка.

Вообще тема очень интересная. Я бы попросил поделиться опытом использования ORM в PHP, включая ссылки :]. С 5-й версией, на мой взгляд, это стало как никогда актуально.

P.S. Меня, после общения с псевдо-ORM Propel и ещё парочкой аналогов не перестаёт терзать [дурацкая] идея портирования .NETовских ObjectSpaces с MS'овским OPath.
 

Alexandre

PHPПенсионер
Меня, после общения с псевдо-ORM Propel и ещё парочкой аналогов не перестаёт терзать [дурацкая] идея портирования .NETовских ObjectSpaces с MS'овским OPath.
Хотя, чего дейсствительно не хватает в пхп для полного счастья ООПешника,так это пакетов. Чтоб одинаковые имена классов могли бы использоваться в разных пакетах, ну и соответственно должен быть механизм их импортирования.

Идея Мs-Nemespaces или java-пакетов это отличная идея, по моему ИМХО явно его не хватает в пхп.

Дурацкая это затея...много возни, не справлюсь.
 

lovchy

nacido para cifrar
Alexandre
Это планировали, от этого отказались. А вообще, да. Namespac'ы рулят.
 
Как вариант -- проверка на существование отловом исключения при попытке создания резолвом, но это небольшой оверхед + неудобство в расширении.
В данный момент у меня именно такая реализация. Но у меня код на perl-е, поэтому чтоб не отлавливать исключительную ситуацию при попытке создания объекта, пришлось сделать отдельный методы select (int ID).

А перемещение я бы сделал методом композиции.
Так и сделал.

Я уже писал выше, что сам сижу на активной записи. Существует класс, который реализует работу с единичной таблицей и конфигурируется при помощи xml конфига. Классы домена наследуют этот класс и зачастую единственное, что необходимо сделать - переопределить конструктор, указав нужных конфиг. Однако хочется чего-то более мощного, поэтому поглядываю в сторону ORM.
 

lovchy

nacido para cifrar
2NetFly
Ну так какой же это ActiveRecord? Добавить к схеме xml описание реляционной модели и получится классический ORM.
 

[sid]

Новичок
По-моему (ну и не только по-моему :), идеального выбора нет. Все зависит от сложности логики поведения. Фаулер говорит, что самых лучших результатов можно добиться при использовании DomainModel. Но зачем мне его реализовывать если сама по себе доменная логика слишком проста. Наверное, все таки, программирование это искусство, так что надор подходить к каждой задачи индивидуально, что бы получить наилучшую производительность при этом. А если на производительность плевать, то на мой взгляд наилучшим решением будет DataMapper + ActiveRecord или немного расширенный TableGateway!
 
Только заметил новые ответы в теме =)

lovchy, в самом первом сообщении я написал, что "использую гибрид паттерна активная запись и ORM". Из ORM у меня позаимствовано конфигурирование при помощи схемы, но это не основная его фича. Мапперов у меня нет как таковых, просто есть базовый класс, который упрощает построение классов с применением активной записи.
 

fisher

накатила суть
>>Кстати, Фаулер не считает зазорным использование в
>>проекте одновременно обоих паттернов
пусть ваш фаулер попробует поддерживать большой проект с такой реализацией, теоретик хренов
 

[sid]

Новичок
Автор оригинала: fisher
>>Кстати, Фаулер не считает зазорным использование в
>>проекте одновременно обоих паттернов
пусть ваш фаулер попробует поддерживать большой проект с такой реализацией, теоретик хренов
Я так думаю... Нет не так... Мне кажется... Опять не так... А вот... Я УВЕРЕН, что у товарища Мартина опыт по созданию и сопровождению приложений корпоративного уровня гораздо больше, чем у всех нас вместе взятых. Если быть точным, то уже больше 20 лет! А еще я думаю, что неверно (по-крайней мере для меня) игнорировать чужой опыт и отвергать чужие идеи. Истиная сущность того или иного архитектурного решения познается, что называется, "в бою". Ну и конечно же если вы ищете панацею от всех проблем, то вы не по тому адресу обратились. Паттерны вам не помогут. Паттерны - это "не конечный пункт назначения, а отлиная отправная точка". Так что программирование - это все таки процесс творческий!
 

fisher

накатила суть
sid: речь шла только о незазорности смеси двух подходов, не более. фаулер безусловно авторитет, но как только объектные граждане начинают городить абстракции для работы субд - сам чорт ногу сломит.
 

[sid]

Новичок
Ну, тут конечно не могу с вами не согласиться, уважаемый fisher! Действительно все эти скопища классов заставляют порядочно полысеть :) Но ведь нет худа без добра. Когда начинаешь во всем этом разбираться, получается что эти паттерны экономят много времени и сил. Ну а обучение это процесс конечно тяжелый, но вместе с тем и индивидуальный, поэтому тут единого рецепта нет. Одним словом, кто сможет пройти через терни запутанности паттернов к их осмыслению тот получит неоцинемый инструментарий в свое распоряжение - этого еще никто не отрицал. И конечно же такие ситуации когда смесь двух подходов "незазорна" - это правильно. Потому что исходя из спецфики задачи можно выбрать разные архитектурные решения (опять таки не забываем, что проектировщик выбирает на основе своего субъективного опыта)!
 

fisher

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

pachanga

Новичок
Автор оригинала: fisher
проектировщик выбирает, а программист программирует и не всегда это одно и то же лицо.
Да не уйдем мы во флуд, но я категорически против разделения на проектировщика и программиста. Практически любой новый дизайн(ну если конечно проектировщик не гений) после получасовой разработки выбрасыватся в корзину....

-~{}~ 10.06.05 12:27:

Автор оригинала: fisher
sid: речь шла только о незазорности смеси двух подходов, не более. фаулер безусловно авторитет, но как только объектные граждане начинают городить абстракции для работы субд - сам чорт ногу сломит.
Согласен, неумелое использование ООП может здорово навредить. Но ведь так в любом деле, никто же не говорит, что ООП панацея....

А насчет использования нескольких подходов, я думаю Фаулер очень даже прав. Для разных задач, по-моему, следует использовать различные методы, если того требуют обстоятельства.
Скажем, для некоторой ресурсоемкой пакетной обработки может быть просто неприемлемым использования DataMapper из-за временных ограничений, когда ActiveRecord или даже прямой SQL запрос окажутся более подходящими.
 
Сверху